mirror of
https://github.com/coder/code-server.git
synced 2026-05-06 12:31:58 +02:00
chore(vscode): update to 1.55.2
This commit is contained in:
@@ -15,12 +15,11 @@ import { getExtensionId } from 'vs/platform/extensionManagement/common/extension
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { canExecuteOnWorkspace } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { ExtensionKindController } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IExtensionManifest } from 'vs/workbench/workbench.web.api';
|
||||
|
||||
|
||||
@@ -78,19 +77,22 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService
|
||||
|
||||
private _location: string | undefined;
|
||||
|
||||
private readonly _extensionKindController: ExtensionKindController;
|
||||
|
||||
constructor(
|
||||
@IExtensionManagementService extensionManagementService: IExtensionManagementService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IProductService productService: IProductService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IExtensionGalleryService extensionGalleryService: IExtensionGalleryService,
|
||||
@ILocalizationsService localizationsService: ILocalizationsService,
|
||||
@ILabelService labelService: ILabelService,
|
||||
@IWorkbenchEnvironmentService envService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(extensionManagementService, extensionGalleryService, localizationsService);
|
||||
super(extensionManagementService, extensionGalleryService);
|
||||
|
||||
const remoteAuthority = envService.remoteAuthority;
|
||||
this._location = remoteAuthority ? labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority) : undefined;
|
||||
|
||||
this._extensionKindController = new ExtensionKindController(productService, configurationService);
|
||||
}
|
||||
|
||||
protected get location(): string | undefined {
|
||||
@@ -98,7 +100,7 @@ class RemoteExtensionCLIManagementService extends ExtensionManagementCLIService
|
||||
}
|
||||
|
||||
protected validateExtensionKind(manifest: IExtensionManifest, output: CLIOutput): boolean {
|
||||
if (!canExecuteOnWorkspace(manifest, this.productService, this.configurationService)) {
|
||||
if (!this._extensionKindController.canExecuteOnWorkspace(manifest)) {
|
||||
output.log(localize('cannot be installed', "Cannot install the '{0}' extension because it is declared to not run in this setup.", getExtensionId(manifest.publisher, manifest.name)));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
* 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 { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IActiveCodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser';
|
||||
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { reviveWebviewContentOptions } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import { ExtHostContext, ExtHostEditorInsetsShape, IExtHostContext, IWebviewOptions, MainContext, MainThreadEditorInsetsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IWebviewService, WebviewElement } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
|
||||
// todo@jrieken move these things back into something like contrib/insets
|
||||
class EditorWebviewZone implements IViewZone {
|
||||
@@ -70,7 +70,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
this._disposables.dispose();
|
||||
}
|
||||
|
||||
async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void> {
|
||||
async $createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void> {
|
||||
|
||||
let editor: IActiveCodeEditor | undefined;
|
||||
id = id.substr(0, id.indexOf(',')); //todo@jrieken HACK
|
||||
@@ -91,10 +91,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
|
||||
const webview = this._webviewService.createWebviewElement('' + handle, {
|
||||
enableFindWidget: false,
|
||||
}, {
|
||||
allowScripts: options.enableScripts,
|
||||
localResourceRoots: options.localResourceRoots ? options.localResourceRoots.map(uri => URI.revive(uri)) : undefined
|
||||
}, { id: extensionId, location: URI.revive(extensionLocation) });
|
||||
}, reviveWebviewContentOptions(options), { id: extensionId, location: URI.revive(extensionLocation) });
|
||||
|
||||
const webviewZone = new EditorWebviewZone(editor, line, height, webview);
|
||||
|
||||
@@ -117,7 +114,6 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
const inset = this.getInset(handle);
|
||||
this._insets.delete(handle);
|
||||
inset.dispose();
|
||||
|
||||
}
|
||||
|
||||
$setHtml(handle: number, value: string): void {
|
||||
@@ -125,12 +121,9 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
|
||||
inset.webview.html = value;
|
||||
}
|
||||
|
||||
$setOptions(handle: number, options: modes.IWebviewOptions): void {
|
||||
$setOptions(handle: number, options: IWebviewOptions): void {
|
||||
const inset = this.getInset(handle);
|
||||
inset.webview.contentOptions = {
|
||||
...options,
|
||||
localResourceRoots: options.localResourceRoots?.map(components => URI.from(components)),
|
||||
};
|
||||
inset.webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
}
|
||||
|
||||
async $postMessage(handle: number, value: any): Promise<boolean> {
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { multibyteAwareBtoa } from 'vs/base/browser/dom';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { CancelablePromise, createCancelablePromise, timeout } from 'vs/base/common/async';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -13,10 +14,9 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { FileChangesEvent, FileChangeType, FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
@@ -99,11 +99,11 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
this._editorProviders.clear();
|
||||
}
|
||||
|
||||
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void {
|
||||
public $registerTextEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, capabilities: extHostProtocol.CustomTextEditorCapabilities): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Text, reviveWebviewExtension(extensionData), viewType, options, capabilities, true);
|
||||
}
|
||||
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void {
|
||||
public $registerCustomEditorProvider(extensionData: extHostProtocol.WebviewExtensionDescription, viewType: string, options: extHostProtocol.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void {
|
||||
this.registerEditorProvider(CustomEditorModelType.Custom, reviveWebviewExtension(extensionData), viewType, options, {}, supportsMultipleEditorsPerDocument);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
modelType: CustomEditorModelType,
|
||||
extension: WebviewExtensionDescription,
|
||||
viewType: string,
|
||||
options: modes.IWebviewPanelOptions,
|
||||
options: extHostProtocol.IWebviewPanelOptions,
|
||||
capabilities: extHostProtocol.CustomTextEditorCapabilities,
|
||||
supportsMultipleEditorsPerDocument: boolean,
|
||||
): void {
|
||||
@@ -176,7 +176,11 @@ export class MainThreadCustomEditors extends Disposable implements extHostProtoc
|
||||
}
|
||||
|
||||
try {
|
||||
await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, webviewInput.getTitle(), editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options, cancellation);
|
||||
await this._proxyCustomEditors.$resolveWebviewEditor(resource, handle, viewType, {
|
||||
title: webviewInput.getTitle(),
|
||||
webviewOptions: webviewInput.webview.contentOptions,
|
||||
panelOptions: webviewInput.webview.options,
|
||||
}, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), cancellation);
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = this.mainThreadWebview.getWebviewResolvedFailedContent(viewType);
|
||||
@@ -274,6 +278,8 @@ namespace HotExitState {
|
||||
|
||||
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
|
||||
|
||||
#isDisposed = false;
|
||||
|
||||
private _fromBackup: boolean = false;
|
||||
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
||||
private _backupId: string | undefined;
|
||||
@@ -282,9 +288,13 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
private _savePoint: number = -1;
|
||||
private readonly _edits: Array<number> = [];
|
||||
private _isDirtyFromContentChange = false;
|
||||
private _inOrphaned = false;
|
||||
|
||||
private _ongoingSave?: CancelablePromise<void>;
|
||||
|
||||
private readonly _onDidChangeOrphaned = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeOrphaned = this._onDidChangeOrphaned.event;
|
||||
|
||||
public static async create(
|
||||
instantiationService: IInstantiationService,
|
||||
proxy: extHostProtocol.ExtHostCustomEditorsShape,
|
||||
@@ -294,8 +304,13 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
getEditors: () => CustomEditorInput[],
|
||||
cancellation: CancellationToken,
|
||||
_backupFileService: IBackupFileService,
|
||||
) {
|
||||
const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, cancellation);
|
||||
): Promise<MainThreadCustomEditorModel> {
|
||||
const editors = getEditors();
|
||||
let untitledDocumentData: VSBuffer | undefined;
|
||||
if (editors.length !== 0) {
|
||||
untitledDocumentData = editors[0].untitledDocumentData;
|
||||
}
|
||||
const { editable } = await proxy.$createCustomDocument(resource, viewType, options.backupId, untitledDocumentData, cancellation);
|
||||
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, !!options.backupId, editable, getEditors);
|
||||
}
|
||||
|
||||
@@ -321,6 +336,8 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
if (_editable) {
|
||||
this._register(workingCopyService.registerWorkingCopy(this));
|
||||
}
|
||||
|
||||
this._register(_fileService.onDidFilesChange(e => this.onDidFilesChange(e)));
|
||||
}
|
||||
|
||||
get editorResource() {
|
||||
@@ -328,10 +345,14 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.#isDisposed = true;
|
||||
|
||||
if (this._editable) {
|
||||
this._undoService.removeElements(this._editorResource);
|
||||
}
|
||||
|
||||
this._proxy.$disposeCustomDocument(this._editorResource, this._viewType);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -371,6 +392,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return this._fromBackup;
|
||||
}
|
||||
|
||||
public isOrphaned(): boolean {
|
||||
return this._inOrphaned;
|
||||
}
|
||||
|
||||
private isUntitled() {
|
||||
return this._editorResource.scheme === Schemas.untitled;
|
||||
}
|
||||
@@ -383,8 +408,64 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
|
||||
//#endregion
|
||||
|
||||
public isReadonly() {
|
||||
return !this._editable;
|
||||
private async onDidFilesChange(e: FileChangesEvent): Promise<void> {
|
||||
let fileEventImpactsModel = false;
|
||||
let newInOrphanModeGuess: boolean | undefined;
|
||||
|
||||
// If we are currently orphaned, we check if the model file was added back
|
||||
if (this._inOrphaned) {
|
||||
const modelFileAdded = e.contains(this.editorResource, FileChangeType.ADDED);
|
||||
if (modelFileAdded) {
|
||||
newInOrphanModeGuess = false;
|
||||
fileEventImpactsModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we check if the model file was deleted
|
||||
else {
|
||||
const modelFileDeleted = e.contains(this.editorResource, FileChangeType.DELETED);
|
||||
if (modelFileDeleted) {
|
||||
newInOrphanModeGuess = true;
|
||||
fileEventImpactsModel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileEventImpactsModel && this._inOrphaned !== newInOrphanModeGuess) {
|
||||
let newInOrphanModeValidated: boolean = false;
|
||||
if (newInOrphanModeGuess) {
|
||||
// We have received reports of users seeing delete events even though the file still
|
||||
// exists (network shares issue: https://github.com/microsoft/vscode/issues/13665).
|
||||
// Since we do not want to mark the model as orphaned, we have to check if the
|
||||
// file is really gone and not just a faulty file event.
|
||||
await timeout(100);
|
||||
|
||||
if (this.#isDisposed) {
|
||||
newInOrphanModeValidated = true;
|
||||
} else {
|
||||
const exists = await this._fileService.exists(this.editorResource);
|
||||
newInOrphanModeValidated = !exists;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._inOrphaned !== newInOrphanModeValidated && !this.#isDisposed) {
|
||||
this.setOrphaned(newInOrphanModeValidated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setOrphaned(orphaned: boolean): void {
|
||||
if (this._inOrphaned !== orphaned) {
|
||||
this._inOrphaned = orphaned;
|
||||
this._onDidChangeOrphaned.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public isEditable(): boolean {
|
||||
return this._editable;
|
||||
}
|
||||
|
||||
public isOnReadonlyFileSystem(): boolean {
|
||||
return this._fileService.hasCapability(this.editorResource, FileSystemProviderCapabilities.Readonly);
|
||||
}
|
||||
|
||||
public get viewType() {
|
||||
|
||||
@@ -142,7 +142,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
} else if (dto.type === 'function') {
|
||||
this.debugService.addFunctionBreakpoint(dto.functionName, dto.id);
|
||||
} else if (dto.type === 'data') {
|
||||
this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes);
|
||||
this.debugService.addDataBreakpoint(dto.label, dto.dataId, dto.canPersist, dto.accessTypes, dto.accessType);
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
|
||||
@@ -202,12 +202,12 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen
|
||||
}
|
||||
|
||||
private _onModelModeChanged(event: { model: ITextModel; oldModeId: string; }): void {
|
||||
let { model, oldModeId } = event;
|
||||
let { model } = event;
|
||||
const modelUrl = model.uri;
|
||||
if (!this._modelIsSynced.has(modelUrl.toString())) {
|
||||
return;
|
||||
}
|
||||
this._proxy.$acceptModelModeChanged(model.uri, oldModeId, model.getLanguageIdentifier().language);
|
||||
this._proxy.$acceptModelModeChanged(model.uri, model.getLanguageIdentifier().language);
|
||||
}
|
||||
|
||||
private _onModelRemoved(modelUrl: URI): void {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
@@ -106,26 +106,26 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
|
||||
private async _handleMissingNotInstalledDependency(extension: IExtensionDescription, missingDependency: string): Promise<void> {
|
||||
const extName = extension.displayName || extension.name;
|
||||
const dependencyExtension = (await this._extensionsWorkbenchService.queryGallery({ names: [missingDependency] }, CancellationToken.None)).firstPage[0];
|
||||
let dependencyExtension: IExtension | null = null;
|
||||
try {
|
||||
dependencyExtension = (await this._extensionsWorkbenchService.queryGallery({ names: [missingDependency] }, CancellationToken.None)).firstPage[0];
|
||||
} catch (err) {
|
||||
}
|
||||
if (dependencyExtension) {
|
||||
this._notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('uninstalledDep', "Cannot activate the '{0}' extension because it depends on the '{1}' extension, which is not installed. Would you like to install the extension and reload the window?", extName, dependencyExtension.displayName),
|
||||
actions: {
|
||||
primary: [new Action('install', localize('install missing dep', "Install and Reload"), '', true,
|
||||
() => this._extensionsWorkbenchService.install(dependencyExtension)
|
||||
() => this._extensionsWorkbenchService.install(dependencyExtension!)
|
||||
.then(() => this._hostService.reload(), e => this._notificationService.error(e)))]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this._notificationService.error(localize('unknownDep', "Cannot activate the '{0}' extension because it depends on an unknown '{1}' extension .", extName, missingDependency));
|
||||
this._notificationService.error(localize('unknownDep', "Cannot activate the '{0}' extension because it depends on an unknown '{1}' extension.", extName, missingDependency));
|
||||
}
|
||||
}
|
||||
|
||||
async $onExtensionHostExit(code: number): Promise<void> {
|
||||
this._extensionService._onExtensionHostExit(code);
|
||||
}
|
||||
|
||||
async $setPerformanceMarks(marks: PerformanceMark[]): Promise<void> {
|
||||
if (this._extensionHostKind === ExtensionHostKind.LocalProcess) {
|
||||
this._timerService.setPerformanceMarks('localExtHost', marks);
|
||||
|
||||
@@ -66,9 +66,12 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape {
|
||||
primaryActions.push(new MessageItemAction('_extension_message_handle_' + command.handle, command.title, command.handle));
|
||||
});
|
||||
|
||||
let source: string | undefined;
|
||||
let source: string | { label: string, id: string } | undefined;
|
||||
if (extension) {
|
||||
source = nls.localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
|
||||
source = {
|
||||
label: nls.localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name),
|
||||
id: extension.identifier.value
|
||||
};
|
||||
}
|
||||
|
||||
if (!source) {
|
||||
|
||||
@@ -3,56 +3,54 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { diffMaps, diffSets } from 'vs/base/common/collections';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { combinedDisposable, Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { EditorActivation, ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { EditorActivation, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { BoundModelReferenceCollection } from 'vs/workbench/api/browser/mainThreadDocuments';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { getNotebookEditorFromEditorPane, IActiveNotebookEditor, INotebookEditor, NotebookEditorOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
|
||||
import { NotebookEditorInput } from 'vs/workbench/contrib/notebook/browser/notebookEditorInput';
|
||||
import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/notebookEditorService';
|
||||
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService';
|
||||
import { ICellEditOperation, ICellRange, IEditor, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ICellEditOperation, ICellRange, IImmediateCellEditOperation, IMainCellDto, INotebookDecorationRenderOptions, INotebookDocumentFilter, INotebookExclusiveDocumentFilter, INotebookKernel, NotebookCellsChangeType, NotebookDataDto, TransientMetadata, TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookEditorModelResolverService } from 'vs/workbench/contrib/notebook/common/notebookEditorModelResolverService';
|
||||
import { IMainNotebookController, INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { IEditorGroup, IEditorGroupsService, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { ExtHostContext, ExtHostNotebookShape, IExtHostContext, INotebookCellStatusBarEntryDto, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookModelAddedData, MainContext, MainThreadNotebookShape, NotebookEditorRevealType, NotebookExtensionDescription } from '../common/extHost.protocol';
|
||||
|
||||
class DocumentAndEditorState {
|
||||
static compute(before: DocumentAndEditorState | undefined, after: DocumentAndEditorState): INotebookDocumentsAndEditorsDelta {
|
||||
class NotebookAndEditorState {
|
||||
static compute(before: NotebookAndEditorState | undefined, after: NotebookAndEditorState): INotebookDocumentsAndEditorsDelta {
|
||||
if (!before) {
|
||||
return {
|
||||
addedDocuments: [...after.documents].map(DocumentAndEditorState._asModelAddData),
|
||||
addedEditors: [...after.textEditors.values()].map(DocumentAndEditorState._asEditorAddData),
|
||||
addedDocuments: [...after.documents].map(NotebookAndEditorState._asModelAddData),
|
||||
addedEditors: [...after.textEditors.values()].map(NotebookAndEditorState._asEditorAddData),
|
||||
visibleEditors: [...after.visibleEditors].map(editor => editor[0])
|
||||
};
|
||||
}
|
||||
const documentDelta = diffSets(before.documents, after.documents);
|
||||
const editorDelta = diffMaps(before.textEditors, after.textEditors);
|
||||
const addedAPIEditors = editorDelta.added.map(DocumentAndEditorState._asEditorAddData);
|
||||
const addedAPIEditors = editorDelta.added.map(NotebookAndEditorState._asEditorAddData);
|
||||
|
||||
const removedAPIEditors = editorDelta.removed.map(removed => removed.getId());
|
||||
|
||||
// const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
|
||||
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
|
||||
|
||||
const visibleEditorDelta = diffMaps(before.visibleEditors, after.visibleEditors);
|
||||
|
||||
return {
|
||||
addedDocuments: documentDelta.added.map(DocumentAndEditorState._asModelAddData),
|
||||
addedDocuments: documentDelta.added.map(NotebookAndEditorState._asModelAddData),
|
||||
removedDocuments: documentDelta.removed.map(e => e.uri),
|
||||
addedEditors: addedAPIEditors,
|
||||
removedEditors: removedAPIEditors,
|
||||
@@ -65,9 +63,9 @@ class DocumentAndEditorState {
|
||||
|
||||
constructor(
|
||||
readonly documents: Set<NotebookTextModel>,
|
||||
readonly textEditors: Map<string, IEditor>,
|
||||
readonly textEditors: Map<string, IActiveNotebookEditor>,
|
||||
readonly activeEditor: string | null | undefined,
|
||||
readonly visibleEditors: Map<string, IEditor>
|
||||
readonly visibleEditors: Map<string, IActiveNotebookEditor>
|
||||
) {
|
||||
//
|
||||
}
|
||||
@@ -87,71 +85,72 @@ class DocumentAndEditorState {
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
})),
|
||||
contentOptions: e.transientOptions,
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
private static _asEditorAddData(add: IEditor): INotebookEditorAddData {
|
||||
private static _asEditorAddData(add: IActiveNotebookEditor): INotebookEditorAddData {
|
||||
return {
|
||||
id: add.getId(),
|
||||
documentUri: add.uri!,
|
||||
documentUri: add.viewModel.uri,
|
||||
selections: add.getSelections(),
|
||||
visibleRanges: add.visibleRanges
|
||||
visibleRanges: add.visibleRanges,
|
||||
viewColumn: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNotebook)
|
||||
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
|
||||
export class MainThreadNotebooks implements MainThreadNotebookShape {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private readonly _notebookProviders = new Map<string, { controller: IMainNotebookController, disposable: IDisposable }>();
|
||||
private readonly _notebookSerializer = new Map<number, IDisposable>();
|
||||
private readonly _notebookKernelProviders = new Map<number, { extension: NotebookExtensionDescription, emitter: Emitter<URI | undefined>, provider: IDisposable }>();
|
||||
private readonly _toDisposeOnEditorRemove = new Map<string, IDisposable>();
|
||||
private readonly _editorEventListenersMapping: Map<string, DisposableStore> = new Map();
|
||||
private readonly _documentEventListenersMapping: ResourceMap<DisposableStore> = new ResourceMap();
|
||||
private readonly _cellStatusBarEntries: Map<number, IDisposable> = new Map();
|
||||
private readonly _editorEventListenersMapping = new Map<string, DisposableStore>();
|
||||
private readonly _documentEventListenersMapping = new ResourceMap<DisposableStore>();
|
||||
private readonly _cellStatusBarEntries = new Map<number, IDisposable>();
|
||||
private readonly _modelReferenceCollection: BoundModelReferenceCollection;
|
||||
|
||||
private _currentState?: DocumentAndEditorState;
|
||||
private _currentState?: NotebookAndEditorState;
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IWorkingCopyService private readonly _workingCopyService: IWorkingCopyService,
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@INotebookService private readonly _notebookService: INotebookService,
|
||||
@INotebookEditorService private readonly _notebookEditorService: INotebookEditorService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupsService: IEditorGroupsService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@INotebookCellStatusBarService private readonly _cellStatusBarService: INotebookCellStatusBarService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookModelResolverService: INotebookEditorModelResolverService,
|
||||
@INotebookEditorModelResolverService private readonly _notebookEditorModelResolverService: INotebookEditorModelResolverService,
|
||||
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostNotebook);
|
||||
this._modelReferenceCollection = new BoundModelReferenceCollection(this._uriIdentityService.extUri);
|
||||
this.registerListeners();
|
||||
this._registerListeners();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
this._disposables.dispose();
|
||||
|
||||
this._modelReferenceCollection.dispose();
|
||||
|
||||
// remove all notebook providers
|
||||
for (let item of this._notebookProviders.values()) {
|
||||
for (const item of this._notebookProviders.values()) {
|
||||
item.disposable.dispose();
|
||||
}
|
||||
|
||||
// remove all kernel providers
|
||||
for (let item of this._notebookKernelProviders.values()) {
|
||||
for (const item of this._notebookKernelProviders.values()) {
|
||||
item.emitter.dispose();
|
||||
item.provider.dispose();
|
||||
}
|
||||
dispose(this._notebookSerializer.values());
|
||||
dispose(this._editorEventListenersMapping.values());
|
||||
dispose(this._documentEventListenersMapping.values());
|
||||
dispose(this._toDisposeOnEditorRemove.values());
|
||||
dispose(this._cellStatusBarEntries.values());
|
||||
}
|
||||
|
||||
@@ -160,51 +159,27 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
if (!textModel) {
|
||||
return false;
|
||||
}
|
||||
return textModel.applyEdits(modelVersionId, cellEdits, true, undefined, () => undefined, undefined);
|
||||
if (textModel.versionId !== modelVersionId) {
|
||||
return false;
|
||||
}
|
||||
return textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined);
|
||||
}
|
||||
|
||||
private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) {
|
||||
if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
|
||||
return false;
|
||||
async $applyEdits(resource: UriComponents, cellEdits: IImmediateCellEditOperation[], computeUndoRedo = true): Promise<void> {
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
if (!textModel) {
|
||||
throw new Error(`Can't apply edits to unknown notebook model: ${resource}`);
|
||||
}
|
||||
|
||||
if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
textModel.applyEdits(cellEdits, true, undefined, () => undefined, undefined, computeUndoRedo);
|
||||
}
|
||||
|
||||
private _emitDelta(delta: INotebookDocumentsAndEditorsDelta) {
|
||||
if (this._isDeltaEmpty(delta)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this._proxy.$acceptDocumentAndEditorsDelta(delta);
|
||||
}
|
||||
|
||||
registerListeners() {
|
||||
private _registerListeners(): void {
|
||||
|
||||
// forward changes to dirty state
|
||||
// todo@rebornix todo@mjbvz this seem way too complicated... is there an easy way to
|
||||
// the actual resource from a working copy?
|
||||
this._register(this._workingCopyService.onDidChangeDirty(e => {
|
||||
this._disposables.add(this._workingCopyService.onDidChangeDirty(e => {
|
||||
if (e.resource.scheme !== Schemas.vscodeNotebook) {
|
||||
return;
|
||||
}
|
||||
@@ -216,16 +191,13 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
}));
|
||||
|
||||
this._notebookService.listNotebookEditors().forEach((e) => {
|
||||
this._addNotebookEditor(e);
|
||||
});
|
||||
|
||||
this._register(this._notebookService.onDidChangeActiveEditor(e => {
|
||||
this._disposables.add(this._editorService.onDidActiveEditorChange(e => {
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onDidChangeVisibleEditors(e => {
|
||||
if (this._notebookProviders.size > 0) {
|
||||
this._disposables.add(this._editorService.onDidVisibleEditorsChange(e => {
|
||||
if (this._notebookProviders.size > 0) { // TODO@rebornix propably wrong, what about providers from another host
|
||||
if (!this._currentState) {
|
||||
// no current state means we didn't even create editors in ext host yet.
|
||||
return;
|
||||
@@ -236,38 +208,49 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
}));
|
||||
|
||||
const notebookEditorAddedHandler = (editor: IEditor) => {
|
||||
if (!this._editorEventListenersMapping.has(editor.getId())) {
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(editor.onDidChangeVisibleRanges(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges }, selections: null });
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeSelection(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: null, selections: { selections: editor.getSelections() } });
|
||||
}));
|
||||
|
||||
this._editorEventListenersMapping.set(editor.getId(), disposableStore);
|
||||
const handleNotebookEditorAdded = (editor: INotebookEditor) => {
|
||||
if (this._editorEventListenersMapping.has(editor.getId())) {
|
||||
//todo@jrieken a bug when this happens?
|
||||
return;
|
||||
}
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(editor.onDidChangeVisibleRanges(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { visibleRanges: { ranges: editor.visibleRanges } });
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeSelection(() => {
|
||||
this._proxy.$acceptEditorPropertiesChanged(editor.getId(), { selections: { selections: editor.getSelections() } });
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeKernel(() => {
|
||||
if (!editor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
this._proxy.$acceptNotebookActiveKernelChange({
|
||||
uri: editor.viewModel.uri,
|
||||
providerHandle: editor.activeKernel?.providerHandle,
|
||||
kernelFriendlyId: editor.activeKernel?.friendlyId
|
||||
});
|
||||
}));
|
||||
|
||||
disposableStore.add(editor.onDidChangeModel(() => this._updateState()));
|
||||
disposableStore.add(editor.onDidFocusEditorWidget(() => this._updateState(editor)));
|
||||
|
||||
this._editorEventListenersMapping.set(editor.getId(), disposableStore);
|
||||
|
||||
const activeNotebookEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
this._updateState(activeNotebookEditor);
|
||||
};
|
||||
|
||||
this._register(this._notebookService.onNotebookEditorAdd(editor => {
|
||||
notebookEditorAddedHandler(editor);
|
||||
this._addNotebookEditor(editor);
|
||||
this._notebookEditorService.listNotebookEditors().forEach(handleNotebookEditorAdded);
|
||||
this._disposables.add(this._notebookEditorService.onDidAddNotebookEditor(handleNotebookEditorAdded));
|
||||
|
||||
this._disposables.add(this._notebookEditorService.onDidRemoveNotebookEditor(editor => {
|
||||
this._editorEventListenersMapping.get(editor.getId())?.dispose();
|
||||
this._editorEventListenersMapping.delete(editor.getId());
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onNotebookEditorsRemove(editors => {
|
||||
this._removeNotebookEditor(editors);
|
||||
|
||||
editors.forEach(editor => {
|
||||
this._editorEventListenersMapping.get(editor.getId())?.dispose();
|
||||
this._editorEventListenersMapping.delete(editor.getId());
|
||||
});
|
||||
}));
|
||||
|
||||
this._notebookService.listNotebookEditors().forEach(editor => {
|
||||
notebookEditorAddedHandler(editor);
|
||||
});
|
||||
|
||||
const cellToDto = (cell: NotebookCellTextModel): IMainCellDto => {
|
||||
return {
|
||||
@@ -283,157 +266,136 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
};
|
||||
|
||||
|
||||
const notebookDocumentAddedHandler = (textModel: NotebookTextModel) => {
|
||||
if (!this._documentEventListenersMapping.has(textModel.uri)) {
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(textModel!.onDidChangeContent(event => {
|
||||
const dto = event.rawEvents.map(e => {
|
||||
const data =
|
||||
e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Initialize
|
||||
? {
|
||||
kind: e.kind,
|
||||
versionId: event.versionId,
|
||||
changes: e.changes.map(diff => [diff[0], diff[1], diff[2].map(cell => cellToDto(cell as NotebookCellTextModel))] as [number, number, IMainCellDto[]])
|
||||
}
|
||||
: (
|
||||
e.kind === NotebookCellsChangeType.Move
|
||||
? {
|
||||
kind: e.kind,
|
||||
index: e.index,
|
||||
length: e.length,
|
||||
newIdx: e.newIdx,
|
||||
versionId: event.versionId,
|
||||
cells: e.cells.map(cell => cellToDto(cell as NotebookCellTextModel))
|
||||
}
|
||||
: e
|
||||
);
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO@rebornix, @jrieken
|
||||
* When a document is modified, it will trigger onDidChangeContent events.
|
||||
* The first event listener is this one, which doesn't know if the text model is dirty or not. It can ask `workingCopyService` but get the wrong result
|
||||
* The second event listener is `NotebookEditorModel`, which will then set `isDirty` to `true`.
|
||||
* Since `e.transient` decides if the model should be dirty or not, we will use the same logic here.
|
||||
*/
|
||||
const hasNonTransientEvent = event.rawEvents.find(e => !e.transient);
|
||||
this._proxy.$acceptModelChanged(textModel.uri, {
|
||||
rawEvents: dto,
|
||||
versionId: event.versionId
|
||||
}, !!hasNonTransientEvent);
|
||||
|
||||
const hasDocumentMetadataChangeEvent = event.rawEvents.find(e => e.kind === NotebookCellsChangeType.ChangeDocumentMetadata);
|
||||
if (!!hasDocumentMetadataChangeEvent) {
|
||||
this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata });
|
||||
}
|
||||
}));
|
||||
this._documentEventListenersMapping.set(textModel!.uri, disposableStore);
|
||||
const handleNotebookDocumentAdded = (textModel: NotebookTextModel) => {
|
||||
if (this._documentEventListenersMapping.has(textModel.uri)) {
|
||||
//todo@jrieken a bug when this happens?
|
||||
return;
|
||||
}
|
||||
const disposableStore = new DisposableStore();
|
||||
disposableStore.add(textModel!.onDidChangeContent(event => {
|
||||
const dto = event.rawEvents.map(e => {
|
||||
const data =
|
||||
e.kind === NotebookCellsChangeType.ModelChange || e.kind === NotebookCellsChangeType.Initialize
|
||||
? {
|
||||
kind: e.kind,
|
||||
versionId: event.versionId,
|
||||
changes: e.changes.map(diff => [diff[0], diff[1], diff[2].map(cell => cellToDto(cell as NotebookCellTextModel))] as [number, number, IMainCellDto[]])
|
||||
}
|
||||
: (
|
||||
e.kind === NotebookCellsChangeType.Move
|
||||
? {
|
||||
kind: e.kind,
|
||||
index: e.index,
|
||||
length: e.length,
|
||||
newIdx: e.newIdx,
|
||||
versionId: event.versionId,
|
||||
cells: e.cells.map(cell => cellToDto(cell as NotebookCellTextModel))
|
||||
}
|
||||
: e
|
||||
);
|
||||
|
||||
return data;
|
||||
});
|
||||
|
||||
/**
|
||||
* TODO@rebornix, @jrieken
|
||||
* When a document is modified, it will trigger onDidChangeContent events.
|
||||
* The first event listener is this one, which doesn't know if the text model is dirty or not. It can ask `workingCopyService` but get the wrong result
|
||||
* The second event listener is `NotebookEditorModel`, which will then set `isDirty` to `true`.
|
||||
* Since `e.transient` decides if the model should be dirty or not, we will use the same logic here.
|
||||
*/
|
||||
const hasNonTransientEvent = event.rawEvents.find(e => !e.transient);
|
||||
this._proxy.$acceptModelChanged(textModel.uri, {
|
||||
rawEvents: dto,
|
||||
versionId: event.versionId
|
||||
}, !!hasNonTransientEvent);
|
||||
|
||||
const hasDocumentMetadataChangeEvent = event.rawEvents.find(e => e.kind === NotebookCellsChangeType.ChangeDocumentMetadata);
|
||||
if (!!hasDocumentMetadataChangeEvent) {
|
||||
this._proxy.$acceptDocumentPropertiesChanged(textModel.uri, { metadata: textModel.metadata });
|
||||
}
|
||||
}));
|
||||
this._documentEventListenersMapping.set(textModel!.uri, disposableStore);
|
||||
};
|
||||
|
||||
this._notebookService.listNotebookDocuments().forEach(notebookDocumentAddedHandler);
|
||||
this._register(this._notebookService.onDidAddNotebookDocument(document => {
|
||||
notebookDocumentAddedHandler(document);
|
||||
this._notebookService.listNotebookDocuments().forEach(handleNotebookDocumentAdded);
|
||||
this._disposables.add(this._notebookService.onDidAddNotebookDocument(document => {
|
||||
handleNotebookDocumentAdded(document);
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onDidRemoveNotebookDocument(uri => {
|
||||
this._disposables.add(this._notebookService.onDidRemoveNotebookDocument(uri => {
|
||||
this._documentEventListenersMapping.get(uri)?.dispose();
|
||||
this._documentEventListenersMapping.delete(uri);
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onDidChangeNotebookActiveKernel(e => {
|
||||
this._disposables.add(this._notebookService.onDidChangeNotebookActiveKernel(e => {
|
||||
this._proxy.$acceptNotebookActiveKernelChange(e);
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onNotebookDocumentSaved(e => {
|
||||
this._disposables.add(this._notebookEditorModelResolverService.onDidSaveNotebook(e => {
|
||||
this._proxy.$acceptModelSaved(e);
|
||||
}));
|
||||
|
||||
const activeEditorPane = this._editorService.activeEditorPane as any | undefined;
|
||||
const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined;
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
this._updateState(notebookEditor);
|
||||
}
|
||||
|
||||
private _addNotebookEditor(e: IEditor) {
|
||||
this._toDisposeOnEditorRemove.set(e.getId(), combinedDisposable(
|
||||
e.onDidChangeModel(() => this._updateState()),
|
||||
e.onDidFocusEditorWidget(() => {
|
||||
this._updateState(e);
|
||||
}),
|
||||
));
|
||||
private _updateState(focusedNotebookEditor?: INotebookEditor): void {
|
||||
|
||||
const activeEditorPane = this._editorService.activeEditorPane as any | undefined;
|
||||
const notebookEditor = activeEditorPane?.isNotebookEditor ? activeEditorPane.getControl() : undefined;
|
||||
this._updateState(notebookEditor);
|
||||
}
|
||||
const activeNotebookEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane);
|
||||
let activeEditor = activeNotebookEditor?.hasModel() ? activeNotebookEditor.getId() : null;
|
||||
|
||||
private _removeNotebookEditor(editors: IEditor[]) {
|
||||
editors.forEach(e => {
|
||||
const sub = this._toDisposeOnEditorRemove.get(e.getId());
|
||||
if (sub) {
|
||||
this._toDisposeOnEditorRemove.delete(e.getId());
|
||||
sub.dispose();
|
||||
const editors = new Map<string, IActiveNotebookEditor>();
|
||||
const visibleEditorsMap = new Map<string, IActiveNotebookEditor>();
|
||||
|
||||
for (const editor of this._notebookEditorService.listNotebookEditors()) {
|
||||
if (editor.hasModel()) {
|
||||
editors.set(editor.getId(), editor);
|
||||
}
|
||||
});
|
||||
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
private async _updateState(focusedNotebookEditor?: IEditor) {
|
||||
let activeEditor: string | null = null;
|
||||
|
||||
const activeEditorPane = this._editorService.activeEditorPane as any | undefined;
|
||||
if (activeEditorPane?.isNotebookEditor) {
|
||||
const notebookEditor = (activeEditorPane.getControl() as INotebookEditor);
|
||||
activeEditor = notebookEditor && notebookEditor.hasModel() ? notebookEditor!.getId() : null;
|
||||
}
|
||||
|
||||
const documentEditorsMap = new Map<string, IEditor>();
|
||||
|
||||
const editors = new Map<string, IEditor>();
|
||||
this._notebookService.listNotebookEditors().forEach(editor => {
|
||||
if (editor.textModel) {
|
||||
editors.set(editor.getId(), editor);
|
||||
documentEditorsMap.set(editor.textModel.uri.toString(), editor);
|
||||
this._editorService.visibleEditorPanes.forEach(editorPane => {
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
|
||||
if (notebookEditor?.hasModel() && editors.has(notebookEditor.getId())) {
|
||||
visibleEditorsMap.set(notebookEditor.getId(), notebookEditor);
|
||||
}
|
||||
});
|
||||
|
||||
const visibleEditorsMap = new Map<string, IEditor>();
|
||||
this._editorService.visibleEditorPanes.forEach(editor => {
|
||||
if ((editor as any).isNotebookEditor) {
|
||||
const nbEditorWidget = (editor as any).getControl() as INotebookEditor;
|
||||
if (nbEditorWidget && editors.has(nbEditorWidget.getId())) {
|
||||
visibleEditorsMap.set(nbEditorWidget.getId(), nbEditorWidget);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const documents = new Set<NotebookTextModel>();
|
||||
this._notebookService.listNotebookDocuments().forEach(document => {
|
||||
documents.add(document);
|
||||
});
|
||||
|
||||
if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.textModel) {
|
||||
if (!activeEditor && focusedNotebookEditor?.textModel) {
|
||||
activeEditor = focusedNotebookEditor.getId();
|
||||
}
|
||||
|
||||
// editors always have view model attached, which means there is already a document in exthost.
|
||||
const newState = new DocumentAndEditorState(documents, editors, activeEditor, visibleEditorsMap);
|
||||
const delta = DocumentAndEditorState.compute(this._currentState, newState);
|
||||
// const isEmptyChange = (!delta.addedDocuments || delta.addedDocuments.length === 0)
|
||||
// && (!delta.removedDocuments || delta.removedDocuments.length === 0)
|
||||
// && (!delta.addedEditors || delta.addedEditors.length === 0)
|
||||
// && (!delta.removedEditors || delta.removedEditors.length === 0)
|
||||
// && (delta.newActiveEditor === undefined)
|
||||
const newState = new NotebookAndEditorState(new Set(this._notebookService.listNotebookDocuments()), editors, activeEditor, visibleEditorsMap);
|
||||
const delta = NotebookAndEditorState.compute(this._currentState, newState);
|
||||
|
||||
// if (!isEmptyChange) {
|
||||
this._currentState = newState;
|
||||
this._emitDelta(delta);
|
||||
// }
|
||||
if (!this._isDeltaEmpty(delta)) {
|
||||
return this._proxy.$acceptDocumentAndEditorsDelta(delta);
|
||||
}
|
||||
}
|
||||
|
||||
private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta): boolean {
|
||||
if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, options: {
|
||||
@@ -452,8 +414,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
contentOptions.transientOutputs = newOptions.transientOutputs;
|
||||
},
|
||||
viewOptions: options.viewOptions,
|
||||
openNotebook: async (viewType: string, uri: URI, backupId?: string) => {
|
||||
const data = await this._proxy.$openNotebook(viewType, uri, backupId);
|
||||
open: async (uri: URI, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken) => {
|
||||
const data = await this._proxy.$openNotebook(viewType, uri, backupId, untitledDocumentData, token);
|
||||
return {
|
||||
data,
|
||||
transientOptions: contentOptions
|
||||
@@ -501,6 +463,24 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
}
|
||||
|
||||
$registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions): void {
|
||||
const registration = this._notebookService.registerNotebookSerializer(viewType, extension, {
|
||||
options,
|
||||
dataToNotebook: (data: VSBuffer): Promise<NotebookDataDto> => {
|
||||
return this._proxy.$dataToNotebook(handle, data);
|
||||
},
|
||||
notebookToData: (data: NotebookDataDto): Promise<VSBuffer> => {
|
||||
return this._proxy.$notebookToData(handle, data);
|
||||
}
|
||||
});
|
||||
this._notebookSerializer.set(handle, registration);
|
||||
}
|
||||
|
||||
$unregisterNotebookSerializer(handle: number): void {
|
||||
this._notebookSerializer.get(handle)?.dispose();
|
||||
this._notebookSerializer.delete(handle);
|
||||
}
|
||||
|
||||
async $registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void> {
|
||||
const emitter = new Emitter<URI | undefined>();
|
||||
const that = this;
|
||||
@@ -526,17 +506,18 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
isPreferred: dto.isPreferred,
|
||||
preloads: dto.preloads?.map(u => URI.revive(u)),
|
||||
supportedLanguages: dto.supportedLanguages,
|
||||
implementsInterrupt: dto.implementsInterrupt,
|
||||
resolve: (uri: URI, editorId: string, token: CancellationToken): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.resolveNotebookKernel', uri.path, dto.friendlyId);
|
||||
return this._proxy.$resolveNotebookKernel(handle, editorId, uri, dto.friendlyId, token);
|
||||
},
|
||||
executeNotebookCell: (uri: URI, cellHandle: number | undefined): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.executeNotebookCell', uri.path, dto.friendlyId, cellHandle);
|
||||
return this._proxy.$executeNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellHandle);
|
||||
executeNotebookCellsRequest: (uri: URI, cellRanges: ICellRange[]): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.executeNotebookCell', uri.path, dto.friendlyId, cellRanges);
|
||||
return this._proxy.$executeNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellRanges);
|
||||
},
|
||||
cancelNotebookCell: (uri: URI, cellHandle: number | undefined): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.cancelNotebookCell', uri.path, dto.friendlyId, cellHandle);
|
||||
return this._proxy.$cancelNotebookKernelFromProvider(handle, uri, dto.friendlyId, cellHandle);
|
||||
cancelNotebookCellExecution: (uri: URI, cellRanges: ICellRange[]): Promise<void> => {
|
||||
this._logService.debug('MainthreadNotebooks.cancelNotebookCellExecution', uri.path, dto.friendlyId, cellRanges);
|
||||
return this._proxy.$cancelNotebookCellExecution(handle, uri, dto.friendlyId, cellRanges);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -563,55 +544,53 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
entry?.emitter.fire(uriComponents ? URI.revive(uriComponents) : undefined);
|
||||
}
|
||||
|
||||
async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean> {
|
||||
const editor = this._notebookService.getNotebookEditor(editorId) as INotebookEditor | undefined;
|
||||
if (editor?.isNotebookEditor) {
|
||||
editor.postMessage(forRendererId, value);
|
||||
return true;
|
||||
async $postMessage(id: string, forRendererId: string | undefined, value: any): Promise<boolean> {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (!editor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
editor.postMessage(forRendererId, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
async $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType) {
|
||||
const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id);
|
||||
if (editor && editor.isNotebookEditor) {
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
if (!notebookEditor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const viewModel = notebookEditor.viewModel;
|
||||
const cell = viewModel.viewCells[range.start];
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
async $tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void> {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
if (!notebookEditor.hasModel()) {
|
||||
return;
|
||||
}
|
||||
const viewModel = notebookEditor.viewModel;
|
||||
const cell = viewModel.viewCells[range.start];
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (revealType) {
|
||||
case NotebookEditorRevealType.Default:
|
||||
return notebookEditor.revealCellRangeInView(range);
|
||||
case NotebookEditorRevealType.InCenter:
|
||||
return notebookEditor.revealInCenter(cell);
|
||||
case NotebookEditorRevealType.InCenterIfOutsideViewport:
|
||||
return notebookEditor.revealInCenterIfOutsideViewport(cell);
|
||||
case NotebookEditorRevealType.AtTop:
|
||||
return notebookEditor.revealInViewAtTop(cell);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (revealType) {
|
||||
case NotebookEditorRevealType.Default:
|
||||
return notebookEditor.revealCellRangeInView(range);
|
||||
case NotebookEditorRevealType.InCenter:
|
||||
return notebookEditor.revealInCenter(cell);
|
||||
case NotebookEditorRevealType.InCenterIfOutsideViewport:
|
||||
return notebookEditor.revealInCenterIfOutsideViewport(cell);
|
||||
case NotebookEditorRevealType.AtTop:
|
||||
return notebookEditor.revealInViewAtTop(cell);
|
||||
}
|
||||
}
|
||||
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions) {
|
||||
this._notebookService.registerEditorDecorationType(key, options);
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void {
|
||||
this._notebookEditorService.registerEditorDecorationType(key, options);
|
||||
}
|
||||
|
||||
$removeNotebookEditorDecorationType(key: string) {
|
||||
this._notebookService.removeEditorDecorationType(key);
|
||||
$removeNotebookEditorDecorationType(key: string): void {
|
||||
this._notebookEditorService.removeEditorDecorationType(key);
|
||||
}
|
||||
|
||||
$trySetDecorations(id: string, range: ICellRange, key: string) {
|
||||
const editor = this._notebookService.listNotebookEditors().find(editor => editor.getId() === id);
|
||||
if (editor && editor.isNotebookEditor) {
|
||||
$trySetDecorations(id: string, range: ICellRange, key: string): void {
|
||||
const editor = this._notebookEditorService.getNotebookEditor(id);
|
||||
if (editor) {
|
||||
const notebookEditor = editor as INotebookEditor;
|
||||
notebookEditor.setEditorDecorations(key, range);
|
||||
}
|
||||
@@ -634,25 +613,25 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
|
||||
|
||||
async $tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise<URI> {
|
||||
async $tryOpenDocument(uriComponents: UriComponents): Promise<URI> {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const ref = await this._notebookModelResolverService.resolve(uri, viewType);
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri, undefined);
|
||||
this._modelReferenceCollection.add(uri, ref);
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
async $trySaveDocument(uriComponents: UriComponents) {
|
||||
const uri = URI.revive(uriComponents);
|
||||
|
||||
const ref = await this._notebookModelResolverService.resolve(uri);
|
||||
const ref = await this._notebookEditorModelResolverService.resolve(uri);
|
||||
const saveResult = await ref.object.save();
|
||||
ref.dispose();
|
||||
return saveResult;
|
||||
}
|
||||
|
||||
async $tryShowNotebookDocument(resource: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string> {
|
||||
const editorOptions: ITextEditorOptions = {
|
||||
const editorOptions = new NotebookEditorOptions({
|
||||
cellSelections: options.selection && [options.selection],
|
||||
preserveFocus: options.preserveFocus,
|
||||
pinned: options.pinned,
|
||||
// selection: options.selection,
|
||||
@@ -660,36 +639,13 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
// but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633
|
||||
activation: options.preserveFocus ? EditorActivation.RESTORE : undefined,
|
||||
override: EditorOverride.DISABLED,
|
||||
};
|
||||
});
|
||||
|
||||
const columnArg = viewColumnToEditorGroup(this._editorGroupsService, options.position);
|
||||
|
||||
let group: IEditorGroup | undefined = undefined;
|
||||
|
||||
if (columnArg === SIDE_GROUP) {
|
||||
const direction = preferredSideBySideGroupDirection(this._configurationService);
|
||||
|
||||
let neighbourGroup = this._editorGroupsService.findGroup({ direction });
|
||||
if (!neighbourGroup) {
|
||||
neighbourGroup = this._editorGroupsService.addGroup(this._editorGroupsService.activeGroup, direction);
|
||||
}
|
||||
group = neighbourGroup;
|
||||
} else {
|
||||
group = this._editorGroupsService.getGroup(viewColumnToEditorGroup(this._editorGroupsService, columnArg)) ?? this._editorGroupsService.activeGroup;
|
||||
}
|
||||
|
||||
const input = this._editorService.createEditorInput({ resource: URI.revive(resource), options: editorOptions });
|
||||
|
||||
// TODO: handle options.selection
|
||||
const editorPane = await this._editorService.openEditor(input, { ...options, override: viewType }, group);
|
||||
const notebookEditor = (editorPane as unknown as { isNotebookEditor?: boolean })?.isNotebookEditor ? (editorPane!.getControl() as INotebookEditor) : undefined;
|
||||
const input = NotebookEditorInput.create(this._instantiationService, URI.revive(resource), viewType);
|
||||
const editorPane = await this._editorService.openEditor(input, editorOptions, options.position);
|
||||
const notebookEditor = getNotebookEditorFromEditorPane(editorPane);
|
||||
|
||||
if (notebookEditor) {
|
||||
if (notebookEditor.viewModel && options.selection && notebookEditor.viewModel.viewCells[options.selection.start]) {
|
||||
const focusedCell = notebookEditor.viewModel.viewCells[options.selection.start];
|
||||
notebookEditor.revealInCenterIfOutsideViewport(focusedCell);
|
||||
notebookEditor.focusElement(focusedCell);
|
||||
}
|
||||
return notebookEditor.getId();
|
||||
} else {
|
||||
throw new Error(`Notebook Editor creation failure for documenet ${resource}`);
|
||||
|
||||
@@ -89,6 +89,7 @@ export class MainThreadQuickOpen implements MainThreadQuickOpenShape {
|
||||
const inputOptions: IInputOptions = Object.create(null);
|
||||
|
||||
if (options) {
|
||||
inputOptions.title = options.title;
|
||||
inputOptions.password = options.password;
|
||||
inputOptions.placeHolder = options.placeHolder;
|
||||
inputOptions.valueSelection = options.valueSelection;
|
||||
|
||||
@@ -242,6 +242,7 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
|
||||
delete this._groupsByHandle[handle];
|
||||
this.groups.splice(this.groups.elements.indexOf(group), 1);
|
||||
this._onDidChangeResources.fire();
|
||||
}
|
||||
|
||||
async getOriginalResource(uri: URI): Promise<URI | null> {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IAccessibilityInformation } from 'vs/platform/accessibility/common/acce
|
||||
export class MainThreadStatusBar implements MainThreadStatusBarShape {
|
||||
|
||||
private readonly entries: Map<number, { accessor: IStatusbarEntryAccessor, alignment: MainThreadStatusBarAlignment, priority: number }> = new Map();
|
||||
static readonly CODICON_REGEXP = /\$\((.*?)\)/g;
|
||||
private static readonly CODICON_REGEXP = /\$\((.*?)\)/g;
|
||||
|
||||
constructor(
|
||||
_extHostContext: IExtHostContext,
|
||||
|
||||
@@ -4,24 +4,46 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MainThreadTelemetryShape, MainContext, IExtHostContext } from '../common/extHost.protocol';
|
||||
import { MainThreadTelemetryShape, MainContext, IExtHostContext, ExtHostTelemetryShape, ExtHostContext } from '../common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTelemetry)
|
||||
export class MainThreadTelemetry implements MainThreadTelemetryShape {
|
||||
export class MainThreadTelemetry extends Disposable implements MainThreadTelemetryShape {
|
||||
private readonly _proxy: ExtHostTelemetryShape;
|
||||
|
||||
private static readonly _name = 'pluginHostTelemetry';
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@IEnvironmentService private readonly _environmenService: IEnvironmentService,
|
||||
) {
|
||||
//
|
||||
super();
|
||||
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTelemetry);
|
||||
|
||||
if (!this._environmenService.disableTelemetry) {
|
||||
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectedKeys.includes('telemetry.enableTelemetry')) {
|
||||
this._proxy.$onDidChangeTelemetryEnabled(this.telemetryEnabled);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this._proxy.$initializeTelemetryEnabled(this.telemetryEnabled);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
//
|
||||
private get telemetryEnabled(): boolean {
|
||||
if (this._environmenService.disableTelemetry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!this._configurationService.getValue('telemetry.enableTelemetry');
|
||||
}
|
||||
|
||||
$publicLog(eventName: string, data: any = Object.create(null)): void {
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ITerminalProcessExtHostProxy, ISpawnExtHostProcessRequest, IAvailableShellsRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalConfiguration, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, IExtHostContext, IShellLaunchConfigDto, TerminalLaunchConfig, ITerminalDimensionsDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITerminalExternalLinkProvider, ITerminalLink } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { IShellLaunchConfig, ITerminalDimensions } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { ExtHostContext, ExtHostTerminalServiceShape, IExtHostContext, IShellLaunchConfigDto, ITerminalDimensionsDto, MainContext, MainThreadTerminalServiceShape, TerminalIdentifier, TerminalLaunchConfig } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { ITerminalExternalLinkProvider, ITerminalInstance, ITerminalInstanceService, ITerminalLink, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { IAvailableProfilesRequest as IAvailableProfilesRequest, IDefaultShellAndArgsRequest, IStartExtensionTerminalRequest, ITerminalProcessExtHostProxy } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { ExtensionHostKind } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
|
||||
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
|
||||
@@ -33,6 +33,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
private readonly _toDispose = new DisposableStore();
|
||||
private readonly _terminalProcessProxies = new Map<number, ITerminalProcessExtHostProxy>();
|
||||
private _dataEventTracker: TerminalDataEventTracker | undefined;
|
||||
private _extHostKind: ExtensionHostKind;
|
||||
/**
|
||||
* A single shared terminal link provider for the exthost. When an ext registers a link
|
||||
* provider, this is registered with the terminal on the renderer side and all links are
|
||||
@@ -48,7 +49,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
|
||||
@@ -60,16 +60,17 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
this._onInstanceDimensionsChanged(instance);
|
||||
}));
|
||||
|
||||
this._extHostKind = extHostContext.extensionHostKind;
|
||||
|
||||
this._toDispose.add(_terminalService.onInstanceDisposed(instance => this._onTerminalDisposed(instance)));
|
||||
this._toDispose.add(_terminalService.onInstanceProcessIdReady(instance => this._onTerminalProcessIdReady(instance)));
|
||||
this._toDispose.add(_terminalService.onInstanceDimensionsChanged(instance => this._onInstanceDimensionsChanged(instance)));
|
||||
this._toDispose.add(_terminalService.onInstanceMaximumDimensionsChanged(instance => this._onInstanceMaximumDimensionsChanged(instance)));
|
||||
this._toDispose.add(_terminalService.onInstanceRequestSpawnExtHostProcess(request => this._onRequestSpawnExtHostProcess(request)));
|
||||
this._toDispose.add(_terminalService.onInstanceRequestStartExtensionTerminal(e => this._onRequestStartExtensionTerminal(e)));
|
||||
this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.id : null)));
|
||||
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.id, instance.title)));
|
||||
this._toDispose.add(_terminalService.onActiveInstanceChanged(instance => this._onActiveTerminalChanged(instance ? instance.instanceId : null)));
|
||||
this._toDispose.add(_terminalService.onInstanceTitleChanged(instance => instance && this._onTitleChanged(instance.instanceId, instance.title)));
|
||||
this._toDispose.add(_terminalService.configHelper.onWorkspacePermissionsChanged(isAllowed => this._onWorkspacePermissionsChanged(isAllowed)));
|
||||
this._toDispose.add(_terminalService.onRequestAvailableShells(e => this._onRequestAvailableShells(e)));
|
||||
this._toDispose.add(_terminalService.onRequestAvailableProfiles(e => this._onRequestAvailableProfiles(e)));
|
||||
|
||||
// ITerminalInstanceService listeners
|
||||
if (terminalInstanceService.onRequestDefaultShellAndArgs) {
|
||||
@@ -83,7 +84,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
});
|
||||
const activeInstance = this._terminalService.getActiveInstance();
|
||||
if (activeInstance) {
|
||||
this._proxy.$acceptActiveTerminalChanged(activeInstance.id);
|
||||
this._proxy.$acceptActiveTerminalChanged(activeInstance.instanceId);
|
||||
}
|
||||
if (this._environmentVariableService.collections.size > 0) {
|
||||
const collectionAsArray = [...this._environmentVariableService.collections.entries()];
|
||||
@@ -130,13 +131,13 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
env: launchConfig.env,
|
||||
strictEnv: launchConfig.strictEnv,
|
||||
hideFromUser: launchConfig.hideFromUser,
|
||||
isExtensionTerminal: launchConfig.isExtensionTerminal,
|
||||
isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal,
|
||||
isExtensionCustomPtyTerminal: launchConfig.isExtensionCustomPtyTerminal,
|
||||
extHostTerminalId: extHostTerminalId,
|
||||
isFeatureTerminal: launchConfig.isFeatureTerminal
|
||||
isFeatureTerminal: launchConfig.isFeatureTerminal,
|
||||
isExtensionOwnedTerminal: launchConfig.isExtensionOwnedTerminal
|
||||
};
|
||||
const terminal = this._terminalService.createTerminal(shellLaunchConfig);
|
||||
this._extHostTerminalIds.set(extHostTerminalId, terminal.id);
|
||||
this._extHostTerminalIds.set(extHostTerminalId, terminal.instanceId);
|
||||
}
|
||||
|
||||
public $show(id: TerminalIdentifier, preserveFocus: boolean): void {
|
||||
@@ -150,7 +151,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
public $hide(id: TerminalIdentifier): void {
|
||||
const rendererId = this._getTerminalId(id);
|
||||
const instance = this._terminalService.getActiveInstance();
|
||||
if (instance && instance.id === rendererId) {
|
||||
if (instance && instance.instanceId === rendererId) {
|
||||
this._terminalService.hidePanel();
|
||||
}
|
||||
}
|
||||
@@ -176,7 +177,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
});
|
||||
// Send initial events if they exist
|
||||
this._terminalService.terminalInstances.forEach(t => {
|
||||
t.initialDataEvents?.forEach(d => this._onTerminalData(t.id, d));
|
||||
t.initialDataEvents?.forEach(d => this._onTerminalData(t.instanceId, d));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -219,7 +220,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
}
|
||||
|
||||
private _onTerminalDisposed(terminalInstance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalClosed(terminalInstance.id, terminalInstance.exitCode);
|
||||
this._proxy.$acceptTerminalClosed(terminalInstance.instanceId, terminalInstance.exitCode);
|
||||
}
|
||||
|
||||
private _onTerminalOpened(terminalInstance: ITerminalInstance): void {
|
||||
@@ -232,63 +233,28 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
env: terminalInstance.shellLaunchConfig.env,
|
||||
hideFromUser: terminalInstance.shellLaunchConfig.hideFromUser
|
||||
};
|
||||
this._proxy.$acceptTerminalOpened(terminalInstance.id, extHostTerminalId, terminalInstance.title, shellLaunchConfigDto);
|
||||
this._proxy.$acceptTerminalOpened(terminalInstance.instanceId, extHostTerminalId, terminalInstance.title, shellLaunchConfigDto);
|
||||
}
|
||||
|
||||
private _onTerminalProcessIdReady(terminalInstance: ITerminalInstance): void {
|
||||
if (terminalInstance.processId === undefined) {
|
||||
return;
|
||||
}
|
||||
this._proxy.$acceptTerminalProcessId(terminalInstance.id, terminalInstance.processId);
|
||||
this._proxy.$acceptTerminalProcessId(terminalInstance.instanceId, terminalInstance.processId);
|
||||
}
|
||||
|
||||
private _onInstanceDimensionsChanged(instance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalDimensions(instance.id, instance.cols, instance.rows);
|
||||
this._proxy.$acceptTerminalDimensions(instance.instanceId, instance.cols, instance.rows);
|
||||
}
|
||||
|
||||
private _onInstanceMaximumDimensionsChanged(instance: ITerminalInstance): void {
|
||||
this._proxy.$acceptTerminalMaximumDimensions(instance.id, instance.maxCols, instance.maxRows);
|
||||
this._proxy.$acceptTerminalMaximumDimensions(instance.instanceId, instance.maxCols, instance.maxRows);
|
||||
}
|
||||
|
||||
private _onRequestSpawnExtHostProcess(request: ISpawnExtHostProcessRequest): void {
|
||||
// Only allow processes on remote ext hosts
|
||||
if (!this._remoteAuthority) {
|
||||
return;
|
||||
}
|
||||
|
||||
const proxy = request.proxy;
|
||||
this._terminalProcessProxies.set(proxy.terminalId, proxy);
|
||||
const shellLaunchConfigDto: IShellLaunchConfigDto = {
|
||||
name: request.shellLaunchConfig.name,
|
||||
executable: request.shellLaunchConfig.executable,
|
||||
args: request.shellLaunchConfig.args,
|
||||
cwd: request.shellLaunchConfig.cwd,
|
||||
env: request.shellLaunchConfig.env,
|
||||
flowControl: this._configurationService.getValue<ITerminalConfiguration>(TERMINAL_CONFIG_SECTION).flowControl
|
||||
};
|
||||
|
||||
this._logService.trace('Spawning ext host process', { terminalId: proxy.terminalId, shellLaunchConfigDto, request });
|
||||
this._proxy.$spawnExtHostProcess(
|
||||
proxy.terminalId,
|
||||
shellLaunchConfigDto,
|
||||
request.activeWorkspaceRootUri,
|
||||
request.cols,
|
||||
request.rows,
|
||||
request.isWorkspaceShellAllowed
|
||||
).then(request.callback, request.callback);
|
||||
|
||||
proxy.onAcknowledgeDataEvent(charCount => this._proxy.$acceptProcessAckDataEvent(proxy.terminalId, charCount));
|
||||
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
|
||||
proxy.onResize(dimensions => this._proxy.$acceptProcessResize(proxy.terminalId, dimensions.cols, dimensions.rows));
|
||||
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
|
||||
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId));
|
||||
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.terminalId));
|
||||
proxy.onRequestLatency(() => this._onRequestLatency(proxy.terminalId));
|
||||
}
|
||||
|
||||
private _onRequestStartExtensionTerminal(request: IStartExtensionTerminalRequest): void {
|
||||
const proxy = request.proxy;
|
||||
this._terminalProcessProxies.set(proxy.terminalId, proxy);
|
||||
this._terminalProcessProxies.set(proxy.instanceId, proxy);
|
||||
|
||||
// Note that onReisze is not being listened to here as it needs to fire when max dimensions
|
||||
// change, excluding the dimension override
|
||||
@@ -298,15 +264,15 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
} : undefined;
|
||||
|
||||
this._proxy.$startExtensionTerminal(
|
||||
proxy.terminalId,
|
||||
proxy.instanceId,
|
||||
initialDimensions
|
||||
).then(request.callback);
|
||||
|
||||
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
|
||||
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
|
||||
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId));
|
||||
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.terminalId));
|
||||
proxy.onRequestLatency(() => this._onRequestLatency(proxy.terminalId));
|
||||
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.instanceId, data));
|
||||
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.instanceId, immediate));
|
||||
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.instanceId));
|
||||
proxy.onRequestInitialCwd(() => this._proxy.$acceptProcessRequestInitialCwd(proxy.instanceId));
|
||||
proxy.onRequestLatency(() => this._onRequestLatency(proxy.instanceId));
|
||||
}
|
||||
|
||||
public $sendProcessTitle(terminalId: number, title: string): void {
|
||||
@@ -387,9 +353,9 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
return true;
|
||||
}
|
||||
|
||||
private async _onRequestAvailableShells(req: IAvailableShellsRequest): Promise<void> {
|
||||
if (this._isPrimaryExtHost()) {
|
||||
req.callback(await this._proxy.$getAvailableShells());
|
||||
private async _onRequestAvailableProfiles(req: IAvailableProfilesRequest): Promise<void> {
|
||||
if (this._isPrimaryExtHost() && this._extHostKind !== ExtensionHostKind.LocalWebWorker) {
|
||||
req.callback(await this._proxy.$getAvailableProfiles(req.quickLaunchOnly));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,12 +405,12 @@ class TerminalDataEventTracker extends Disposable {
|
||||
|
||||
this._terminalService.terminalInstances.forEach(instance => this._registerInstance(instance));
|
||||
this._register(this._terminalService.onInstanceCreated(instance => this._registerInstance(instance)));
|
||||
this._register(this._terminalService.onInstanceDisposed(instance => this._bufferer.stopBuffering(instance.id)));
|
||||
this._register(this._terminalService.onInstanceDisposed(instance => this._bufferer.stopBuffering(instance.instanceId)));
|
||||
}
|
||||
|
||||
private _registerInstance(instance: ITerminalInstance): void {
|
||||
// Buffer data events to reduce the amount of messages going to the extension host
|
||||
this._register(this._bufferer.startBuffering(instance.id, instance.onData));
|
||||
this._register(this._bufferer.startBuffering(instance.instanceId, instance.onData));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,13 +422,13 @@ class ExtensionTerminalLinkProvider implements ITerminalExternalLinkProvider {
|
||||
|
||||
async provideLinks(instance: ITerminalInstance, line: string): Promise<ITerminalLink[] | undefined> {
|
||||
const proxy = this._proxy;
|
||||
const extHostLinks = await proxy.$provideLinks(instance.id, line);
|
||||
const extHostLinks = await proxy.$provideLinks(instance.instanceId, line);
|
||||
return extHostLinks.map(dto => ({
|
||||
id: dto.id,
|
||||
startIndex: dto.startIndex,
|
||||
length: dto.length,
|
||||
label: dto.label,
|
||||
activate: () => proxy.$activateLink(instance.id, dto.id)
|
||||
activate: () => proxy.$activateLink(instance.instanceId, dto.id)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,23 +11,25 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { getTestSubscriptionKey, ISerializedTestResults, ITestState, RunTestsRequest, TestDiffOpType, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { HydratedTestResult, ITestResultService, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResultService';
|
||||
import { ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ITestRootProvider, ITestService } from 'vs/workbench/contrib/testing/common/testService';
|
||||
import { ExtHostContext, ExtHostTestingResource, ExtHostTestingShape, IExtHostContext, MainContext, MainThreadTestingShape } from '../common/extHost.protocol';
|
||||
|
||||
const reviveDiff = (diff: TestsDiff) => {
|
||||
for (const entry of diff) {
|
||||
if (entry[0] === TestDiffOpType.Add || entry[0] === TestDiffOpType.Update) {
|
||||
const item = entry[1];
|
||||
if (item.item.location) {
|
||||
item.item.location.uri = URI.revive(item.item.location.uri);
|
||||
item.item.location.range = Range.lift(item.item.location.range);
|
||||
if (item.item?.uri) {
|
||||
item.item.uri = URI.revive(item.item.uri);
|
||||
}
|
||||
if (item.item?.range) {
|
||||
item.item.range = Range.lift(item.item.range);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTesting)
|
||||
export class MainThreadTesting extends Disposable implements MainThreadTestingShape {
|
||||
export class MainThreadTesting extends Disposable implements MainThreadTestingShape, ITestRootProvider {
|
||||
private readonly proxy: ExtHostTestingShape;
|
||||
private readonly testSubscriptions = new Map<string, IDisposable>();
|
||||
private readonly testProviderRegistrations = new Map<string, IDisposable>();
|
||||
@@ -56,7 +58,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
}
|
||||
}));
|
||||
|
||||
testService.updateRootProviderCount(1);
|
||||
this._register(testService.registerRootProvider(this));
|
||||
|
||||
for (const { resource, uri } of this.testService.subscriptions) {
|
||||
this.proxy.$subscribeToTests(resource, uri);
|
||||
@@ -66,25 +68,14 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void {
|
||||
public $publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void {
|
||||
this.resultService.push(new HydratedTestResult(results, persist));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$retireTest(extId: string): void {
|
||||
for (const result of this.resultService.results) {
|
||||
if (result instanceof LiveTestResult) {
|
||||
result.retire(extId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void {
|
||||
public $updateTestStateInRun(runId: string, testId: string, state: ITestState): void {
|
||||
const r = this.resultService.getResult(runId);
|
||||
if (r && r instanceof LiveTestResult) {
|
||||
for (const message of state.messages) {
|
||||
@@ -105,6 +96,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
const disposable = this.testService.registerTestController(id, {
|
||||
runTests: (req, token) => this.proxy.$runTestsForProvider(req, token),
|
||||
lookupTest: test => this.proxy.$lookupTest(test),
|
||||
expandTest: (src, levels) => this.proxy.$expandTest(src, isFinite(levels) ? levels : -1),
|
||||
});
|
||||
|
||||
this.testProviderRegistrations.set(id, disposable);
|
||||
@@ -121,7 +113,7 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
$subscribeToDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void {
|
||||
public $subscribeToDiffs(resource: ExtHostTestingResource, uriComponents: UriComponents): void {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const disposable = this.testService.subscribeToDiffs(resource, uri,
|
||||
diff => this.proxy.$acceptDiff(resource, uriComponents, diff));
|
||||
@@ -152,7 +144,6 @@ export class MainThreadTesting extends Disposable implements MainThreadTestingSh
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
this.testService.updateRootProviderCount(-1);
|
||||
for (const subscription of this.testSubscriptions.values()) {
|
||||
subscription.dispose();
|
||||
}
|
||||
|
||||
@@ -4,22 +4,24 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape, CandidatePortSource } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape, CandidatePortSource, PortAttributesProviderSelector } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged } from 'vs/platform/remote/common/tunnel';
|
||||
import { CandidatePort, IRemoteExplorerService, makeAddress, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT, PORT_AUTO_SOURCE_SETTING_PROCESS } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, isPortPrivileged, ProvidedPortAttributes, PortAttributesProvider } from 'vs/platform/remote/common/tunnel';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTunnelService)
|
||||
export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape {
|
||||
export class MainThreadTunnelService extends Disposable implements MainThreadTunnelServiceShape, PortAttributesProvider {
|
||||
private readonly _proxy: ExtHostTunnelServiceShape;
|
||||
private elevateionRetry: boolean = false;
|
||||
private portsAttributesProviders: Map<number, PortAttributesProviderSelector> = new Map();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@@ -36,20 +38,57 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
this._register(tunnelService.onTunnelClosed(() => this._proxy.$onDidTunnelsChange()));
|
||||
}
|
||||
|
||||
private processFindingEnabled(): boolean {
|
||||
return (!!this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)) && (this.configurationService.getValue(PORT_AUTO_SOURCE_SETTING) === PORT_AUTO_SOURCE_SETTING_PROCESS);
|
||||
}
|
||||
|
||||
async $setRemoteTunnelService(processId: number): Promise<void> {
|
||||
this.remoteExplorerService.namedProcesses.set(processId, 'Code Extension Host');
|
||||
if (this.remoteExplorerService.portsFeaturesEnabled) {
|
||||
this._proxy.$registerCandidateFinder(this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING));
|
||||
this._proxy.$registerCandidateFinder(this.processFindingEnabled());
|
||||
} else {
|
||||
this._register(this.remoteExplorerService.onEnabledPortsFeatures(() => this._proxy.$registerCandidateFinder(this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING))));
|
||||
}
|
||||
this._register(this.configurationService.onDidChangeConfiguration(async (e) => {
|
||||
if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING)) {
|
||||
return this._proxy.$registerCandidateFinder((this.configurationService.getValue(PORT_AUTO_FORWARD_SETTING)));
|
||||
if (e.affectsConfiguration(PORT_AUTO_FORWARD_SETTING) || e.affectsConfiguration(PORT_AUTO_SOURCE_SETTING)) {
|
||||
return this._proxy.$registerCandidateFinder(this.processFindingEnabled());
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private _alreadyRegistered: boolean = false;
|
||||
async $registerPortsAttributesProvider(selector: PortAttributesProviderSelector, providerHandle: number): Promise<void> {
|
||||
this.portsAttributesProviders.set(providerHandle, selector);
|
||||
if (!this._alreadyRegistered) {
|
||||
this.remoteExplorerService.tunnelModel.addAttributesProvider(this);
|
||||
this._alreadyRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
async $unregisterPortsAttributesProvider(providerHandle: number): Promise<void> {
|
||||
this.portsAttributesProviders.delete(providerHandle);
|
||||
}
|
||||
|
||||
async providePortAttributes(ports: number[], pid: number | undefined, commandLine: string | undefined, token: CancellationToken): Promise<ProvidedPortAttributes[]> {
|
||||
if (this.portsAttributesProviders.size === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Check all the selectors to make sure it's worth going to the extension host.
|
||||
const appropriateHandles = Array.from(this.portsAttributesProviders.entries()).filter(entry => {
|
||||
const selector = entry[1];
|
||||
const portRange = selector.portRange;
|
||||
const portInRange = portRange ? ports.some(port => portRange[0] <= port && port < portRange[1]) : true;
|
||||
const pidMatches = !selector.pid || (selector.pid === pid);
|
||||
return portInRange || pidMatches;
|
||||
}).map(entry => entry[0]);
|
||||
|
||||
if (appropriateHandles.length === 0) {
|
||||
return [];
|
||||
}
|
||||
return this._proxy.$providePortAttributes(appropriateHandles, ports, pid, commandLine, token);
|
||||
}
|
||||
|
||||
async $openTunnel(tunnelOptions: TunnelOptions, source: string): Promise<TunnelDto | undefined> {
|
||||
const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remoteAddress, tunnelOptions.localAddressPort, tunnelOptions.label, source, false);
|
||||
if (tunnel) {
|
||||
@@ -102,26 +141,23 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun
|
||||
const tunnelProvider: ITunnelProvider = {
|
||||
forwardPort: (tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions) => {
|
||||
const forward = this._proxy.$forwardPort(tunnelOptions, tunnelCreationOptions);
|
||||
if (forward) {
|
||||
return forward.then(tunnel => {
|
||||
this.logService.trace(`MainThreadTunnelService: New tunnel established by tunnel provider: ${tunnel?.remoteAddress.host}:${tunnel?.remoteAddress.port}`);
|
||||
if (!tunnel) {
|
||||
return undefined;
|
||||
return forward.then(tunnel => {
|
||||
this.logService.trace(`ForwardedPorts: (MainThreadTunnelService) New tunnel established by tunnel provider: ${tunnel?.remoteAddress.host}:${tunnel?.remoteAddress.port}`);
|
||||
if (!tunnel) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
tunnelRemotePort: tunnel.remoteAddress.port,
|
||||
tunnelRemoteHost: tunnel.remoteAddress.host,
|
||||
localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : makeAddress(tunnel.localAddress.host, tunnel.localAddress.port),
|
||||
tunnelLocalPort: typeof tunnel.localAddress !== 'string' ? tunnel.localAddress.port : undefined,
|
||||
public: tunnel.public,
|
||||
dispose: async (silent?: boolean) => {
|
||||
this.logService.trace(`ForwardedPorts: (MainThreadTunnelService) Closing tunnel from tunnel provider: ${tunnel?.remoteAddress.host}:${tunnel?.remoteAddress.port}`);
|
||||
return this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }, silent);
|
||||
}
|
||||
return {
|
||||
tunnelRemotePort: tunnel.remoteAddress.port,
|
||||
tunnelRemoteHost: tunnel.remoteAddress.host,
|
||||
localAddress: typeof tunnel.localAddress === 'string' ? tunnel.localAddress : makeAddress(tunnel.localAddress.host, tunnel.localAddress.port),
|
||||
tunnelLocalPort: typeof tunnel.localAddress !== 'string' ? tunnel.localAddress.port : undefined,
|
||||
public: tunnel.public,
|
||||
dispose: async (silent?: boolean) => {
|
||||
this.logService.trace(`MainThreadTunnelService: Closing tunnel from tunnel provider: ${tunnel?.remoteAddress.host}:${tunnel?.remoteAddress.port}`);
|
||||
return this._proxy.$closeTunnel({ host: tunnel.remoteAddress.host, port: tunnel.remoteAddress.port }, silent);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
this.tunnelService.setTunnelProvider(tunnelProvider, features);
|
||||
|
||||
@@ -7,13 +7,14 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { MainThreadWebviews, reviveWebviewExtension, reviveWebviewOptions } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import { MainThreadWebviews, reviveWebviewContentOptions, reviveWebviewExtension } from 'vs/workbench/api/browser/mainThreadWebviews';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { editorGroupToViewColumn, EditorGroupColumn, IEditorInput, viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { EditorGroupColumn, editorGroupToViewColumn, IEditorInput, viewColumnToEditorGroup } from 'vs/workbench/common/editor';
|
||||
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
|
||||
import { WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webviewPanel/browser/webviewEditorInput';
|
||||
import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { WebviewIcons } from 'vs/workbench/contrib/webviewPanel/browser/webviewIconManager';
|
||||
import { ICreateWebViewShowOptions, IWebviewWorkbenchService } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
@@ -151,9 +152,12 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
extensionData: extHostProtocol.WebviewExtensionDescription,
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
},
|
||||
showOptions: { viewColumn?: EditorGroupColumn, preserveFocus?: boolean; },
|
||||
options: WebviewInputOptions
|
||||
): void {
|
||||
const mainThreadShowOptions: ICreateWebViewShowOptions = Object.create(null);
|
||||
if (showOptions) {
|
||||
@@ -163,7 +167,7 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
|
||||
const extension = reviveWebviewExtension(extensionData);
|
||||
|
||||
const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), title, mainThreadShowOptions, reviveWebviewOptions(options), extension);
|
||||
const webview = this._webviewWorkbenchService.createWebview(handle, this.webviewPanelViewType.fromExternal(viewType), initData.title, mainThreadShowOptions, reviveWebviewOptions(initData.panelOptions), reviveWebviewContentOptions(initData.webviewOptions), extension);
|
||||
this.addWebviewInput(handle, webview);
|
||||
|
||||
/* __GDPR__
|
||||
@@ -231,7 +235,12 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
}
|
||||
|
||||
try {
|
||||
await this._proxy.$deserializeWebviewPanel(handle, viewType, webviewInput.getTitle(), state, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0), webviewInput.webview.options);
|
||||
await this._proxy.$deserializeWebviewPanel(handle, viewType, {
|
||||
title: webviewInput.getTitle(),
|
||||
state,
|
||||
panelOptions: webviewInput.webview.options,
|
||||
webviewOptions: webviewInput.webview.contentOptions,
|
||||
}, editorGroupToViewColumn(this._editorGroupService, webviewInput.group || 0));
|
||||
} catch (error) {
|
||||
onUnexpectedError(error);
|
||||
webviewInput.webview.html = this._mainThreadWebviews.getWebviewResolvedFailedContent(viewType);
|
||||
@@ -324,7 +333,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function reviveWebviewIcon(
|
||||
value: { light: UriComponents, dark: UriComponents; } | undefined
|
||||
): WebviewIcons | undefined {
|
||||
@@ -333,3 +341,9 @@ function reviveWebviewIcon(
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function reviveWebviewOptions(panelOptions: extHostProtocol.IWebviewPanelOptions): WebviewOptions {
|
||||
return {
|
||||
enableFindWidget: panelOptions.enableFindWidget,
|
||||
retainContextWhenHidden: panelOptions.retainContextWhenHidden,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,14 +8,12 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IWebviewOptions } from 'vs/editor/common/modes';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { Webview, WebviewExtensionDescription, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInputOptions } from 'vs/workbench/contrib/webviewPanel/browser/webviewWorkbenchService';
|
||||
import { Webview, WebviewContentOptions, WebviewExtensionDescription, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
|
||||
export class MainThreadWebviews extends Disposable implements extHostProtocol.MainThreadWebviewsShape {
|
||||
|
||||
@@ -42,6 +40,10 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
}
|
||||
|
||||
public addWebview(handle: extHostProtocol.WebviewHandle, webview: WebviewOverlay): void {
|
||||
if (this._webviews.has(handle)) {
|
||||
throw new Error('Webview already registered');
|
||||
}
|
||||
|
||||
this._webviews.set(handle, webview);
|
||||
this.hookupWebviewEventDelegate(handle, webview);
|
||||
}
|
||||
@@ -51,9 +53,9 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
webview.html = value;
|
||||
}
|
||||
|
||||
public $setOptions(handle: extHostProtocol.WebviewHandle, options: IWebviewOptions): void {
|
||||
public $setOptions(handle: extHostProtocol.WebviewHandle, options: extHostProtocol.IWebviewOptions): void {
|
||||
const webview = this.getWebview(handle);
|
||||
webview.contentOptions = reviveWebviewOptions(options);
|
||||
webview.contentOptions = reviveWebviewContentOptions(options);
|
||||
}
|
||||
|
||||
public async $postMessage(handle: extHostProtocol.WebviewHandle, message: any): Promise<boolean> {
|
||||
@@ -116,10 +118,11 @@ export function reviveWebviewExtension(extensionData: extHostProtocol.WebviewExt
|
||||
return { id: extensionData.id, location: URI.revive(extensionData.location) };
|
||||
}
|
||||
|
||||
export function reviveWebviewOptions(options: IWebviewOptions): WebviewInputOptions {
|
||||
export function reviveWebviewContentOptions(webviewOptions: extHostProtocol.IWebviewOptions): WebviewContentOptions {
|
||||
return {
|
||||
...options,
|
||||
allowScripts: options.enableScripts,
|
||||
localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(r => URI.revive(r)) : undefined,
|
||||
allowScripts: webviewOptions.enableScripts,
|
||||
enableCommandUris: webviewOptions.enableCommandUris,
|
||||
localResourceRoots: Array.isArray(webviewOptions.localResourceRoots) ? webviewOptions.localResourceRoots.map(r => URI.revive(r)) : undefined,
|
||||
portMapping: webviewOptions.portMapping,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { WorkspaceTrustStateChangeEvent, IWorkspaceTrustService, WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { WorkspaceTrustStateChangeEvent, IWorkspaceTrustService, WorkspaceTrustRequestOptions, WorkspaceTrustState } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
@@ -208,8 +208,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
|
||||
// --- trust ---
|
||||
|
||||
$requireWorkspaceTrust(modal?: boolean): Promise<WorkspaceTrustState> {
|
||||
return this._workspaceTrustService.requireWorkspaceTrust({ modal: modal ?? true });
|
||||
$requireWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState> {
|
||||
return this._workspaceTrustService.requireWorkspaceTrust(options);
|
||||
}
|
||||
|
||||
private getWorkspaceTrustState(): WorkspaceTrustState {
|
||||
|
||||
@@ -32,7 +32,11 @@ function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) =>
|
||||
|
||||
interface INewWindowAPICommandOptions {
|
||||
reuseWindow?: boolean;
|
||||
remoteAuthority?: string;
|
||||
/**
|
||||
* If set, defines the remoteAuthority of the new window. `null` will open a local window.
|
||||
* If not set, defaults to remoteAuthority of the current window.
|
||||
*/
|
||||
remoteAuthority?: string | null;
|
||||
}
|
||||
|
||||
export class NewWindowAPICommand {
|
||||
@@ -95,6 +99,7 @@ interface RecentEntry {
|
||||
uri: URI;
|
||||
type: 'workspace' | 'folder' | 'file';
|
||||
label?: string;
|
||||
remoteAuthority?: string;
|
||||
}
|
||||
|
||||
CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async function (accessor: ServicesAccessor, recentEntry: RecentEntry) {
|
||||
@@ -102,13 +107,14 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio
|
||||
let recent: IRecent | undefined = undefined;
|
||||
const uri = recentEntry.uri;
|
||||
const label = recentEntry.label;
|
||||
const remoteAuthority = recentEntry.remoteAuthority;
|
||||
if (recentEntry.type === 'workspace') {
|
||||
const workspace = await workspacesService.getWorkspaceIdentifier(uri);
|
||||
recent = { workspace, label };
|
||||
recent = { workspace, label, remoteAuthority };
|
||||
} else if (recentEntry.type === 'folder') {
|
||||
recent = { folderUri: uri, label };
|
||||
recent = { folderUri: uri, label, remoteAuthority };
|
||||
} else {
|
||||
recent = { fileUri: uri, label };
|
||||
recent = { fileUri: uri, label, remoteAuthority };
|
||||
}
|
||||
return workspacesService.addRecentlyOpened([recent]);
|
||||
});
|
||||
|
||||
@@ -7,7 +7,6 @@ import * as nls from 'vs/nls';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -27,7 +26,7 @@ import { ExtHostDocumentContentProvider } from 'vs/workbench/api/common/extHostD
|
||||
import { ExtHostDocumentSaveParticipant } from 'vs/workbench/api/common/extHostDocumentSaveParticipant';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { Extension, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem';
|
||||
import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService';
|
||||
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
|
||||
@@ -52,8 +51,7 @@ import { throwProposedApiError, checkProposedApiEnabled, checkRequiresWorkspaceT
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
|
||||
import type * as vscode from 'vscode';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { originalFSPath } from 'vs/base/common/resources';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { values } from 'vs/base/common/collections';
|
||||
import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets';
|
||||
import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService';
|
||||
@@ -85,6 +83,7 @@ import { ExtHostTesting } from 'vs/workbench/api/common/extHostTesting';
|
||||
import { ExtHostUriOpeners } from 'vs/workbench/api/common/extHostUriOpener';
|
||||
import { IExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
|
||||
import { ExtHostEditorTabs } from 'vs/workbench/api/common/extHostEditorTabs';
|
||||
import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
|
||||
export interface IExtensionApiFactory {
|
||||
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
|
||||
@@ -101,6 +100,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostConsumerFileSystem = accessor.get(IExtHostConsumerFileSystem);
|
||||
const extensionService = accessor.get(IExtHostExtensionService);
|
||||
const extHostWorkspace = accessor.get(IExtHostWorkspace);
|
||||
const extHostTelemetry = accessor.get(IExtHostTelemetry);
|
||||
const extHostConfiguration = accessor.get(IExtHostConfiguration);
|
||||
const uriTransformer = accessor.get(IURITransformerService);
|
||||
const rpcProtocol = accessor.get(IExtHostRpcService);
|
||||
@@ -122,6 +122,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService);
|
||||
rpcProtocol.set(ExtHostContext.ExtHostWindow, extHostWindow);
|
||||
rpcProtocol.set(ExtHostContext.ExtHostSecretState, extHostSecretState);
|
||||
rpcProtocol.set(ExtHostContext.ExtHostTelemetry, extHostTelemetry);
|
||||
|
||||
// automatically create and register addressable instances
|
||||
const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations));
|
||||
@@ -139,7 +140,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
const extHostDocuments = rpcProtocol.set(ExtHostContext.ExtHostDocuments, new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostDocumentContentProviders = rpcProtocol.set(ExtHostContext.ExtHostDocumentContentProviders, new ExtHostDocumentContentProvider(rpcProtocol, extHostDocumentsAndEditors, extHostLogService));
|
||||
const extHostDocumentSaveParticipant = rpcProtocol.set(ExtHostContext.ExtHostDocumentSaveParticipant, new ExtHostDocumentSaveParticipant(extHostLogService, extHostDocuments, rpcProtocol.getProxy(MainContext.MainThreadBulkEdits)));
|
||||
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extHostLogService, extensionStoragePaths));
|
||||
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, extHostDocuments, initData.environment, extHostLogService, extensionStoragePaths));
|
||||
const extHostEditors = rpcProtocol.set(ExtHostContext.ExtHostEditors, new ExtHostEditors(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostTreeViews = rpcProtocol.set(ExtHostContext.ExtHostTreeViews, new ExtHostTreeViews(rpcProtocol.getProxy(MainContext.MainThreadTreeViews), extHostCommands, extHostLogService));
|
||||
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment));
|
||||
@@ -167,7 +168,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
rpcProtocol.assertRegistered(expected);
|
||||
|
||||
// Other instances
|
||||
const extHostBulkEdits = new ExtHostBulkEdits(rpcProtocol, extHostDocumentsAndEditors, extHostNotebook);
|
||||
const extHostBulkEdits = new ExtHostBulkEdits(rpcProtocol, extHostDocumentsAndEditors);
|
||||
const extHostClipboard = new ExtHostClipboard(rpcProtocol);
|
||||
const extHostMessageService = new ExtHostMessageService(rpcProtocol, extHostLogService);
|
||||
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
|
||||
@@ -292,6 +293,16 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
get shell() {
|
||||
return extHostTerminalService.getDefaultShell(false, configProvider);
|
||||
},
|
||||
get isTelemetryEnabled() {
|
||||
return extHostTelemetry.getTelemetryEnabled();
|
||||
},
|
||||
get onDidChangeTelemetryEnabled(): Event<boolean> {
|
||||
return extHostTelemetry.onDidChangeTelemetryEnabled;
|
||||
},
|
||||
get isNewAppInstall() {
|
||||
const installAge = Date.now() - new Date(initData.telemetryInfo.firstSessionDate).getTime();
|
||||
return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // install age is less than a day
|
||||
},
|
||||
openExternal(uri: URI, options?: { allowContributedOpeners?: boolean | string; }) {
|
||||
return extHostWindow.openUri(uri, {
|
||||
allowTunneling: !!initData.remote.authority,
|
||||
@@ -354,14 +365,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
|
||||
// namespace: extensions
|
||||
const extensions: typeof vscode.extensions = {
|
||||
getExtension(extensionId: string): Extension<any> | undefined {
|
||||
getExtension(extensionId: string): vscode.Extension<any> | undefined {
|
||||
const desc = extensionRegistry.getExtensionDescription(extensionId);
|
||||
if (desc) {
|
||||
return new Extension(extensionService, extension.identifier, desc, extensionKind);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
get all(): Extension<any>[] {
|
||||
get all(): vscode.Extension<any>[] {
|
||||
return extensionRegistry.getAllExtensionDescriptions().map((desc) => new Extension(extensionService, extension.identifier, desc, extensionKind));
|
||||
},
|
||||
get onDidChange() {
|
||||
@@ -414,7 +425,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
return extHostLanguageFeatures.registerEvaluatableExpressionProvider(extension, checkSelector(selector), provider, extension.identifier);
|
||||
},
|
||||
registerInlineValuesProvider(selector: vscode.DocumentSelector, provider: vscode.InlineValuesProvider): vscode.Disposable {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostLanguageFeatures.registerInlineValuesProvider(extension, checkSelector(selector), provider, extension.identifier);
|
||||
},
|
||||
registerDocumentHighlightProvider(selector: vscode.DocumentSelector, provider: vscode.DocumentHighlightProvider): vscode.Disposable {
|
||||
@@ -691,9 +701,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables);
|
||||
},
|
||||
showNotebookDocument(document, options?) {
|
||||
showNotebookDocument(uriOrDocument, options?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.showNotebookDocument(document, options);
|
||||
return extHostNotebook.showNotebookDocument(uriOrDocument, options);
|
||||
},
|
||||
registerExternalUriOpener(id: string, opener: vscode.ExternalUriOpener, metadata: vscode.ExternalUriOpenerMetadata) {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -890,6 +900,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.onDidChangeTunnels(listener, thisArg, disposables);
|
||||
},
|
||||
registerPortAttributesProvider: (portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTunnelService.registerPortsAttributesProvider(portSelector, provider);
|
||||
},
|
||||
registerTimelineProvider: (scheme: string | string[], provider: vscode.TimelineProvider) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTimeline.registerTimelineProvider(scheme, provider, extension.identifier, extHostCommands.converter);
|
||||
@@ -899,10 +913,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkRequiresWorkspaceTrust(extension);
|
||||
return extHostWorkspace.trustState;
|
||||
},
|
||||
requireWorkspaceTrust: (modal?: boolean) => {
|
||||
requireWorkspaceTrust: (options?: vscode.WorkspaceTrustRequestOptions) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
checkRequiresWorkspaceTrust(extension);
|
||||
return extHostWorkspace.requireWorkspaceTrust(modal);
|
||||
return extHostWorkspace.requireWorkspaceTrust(options);
|
||||
},
|
||||
onDidChangeWorkspaceTrustState: (listener, thisArgs?, disposables?) => {
|
||||
return extHostWorkspace.onDidChangeWorkspaceTrustState(listener, thisArgs, disposables);
|
||||
@@ -1012,18 +1026,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
};
|
||||
|
||||
// namespace: notebook
|
||||
const notebook: (typeof vscode.notebook & {
|
||||
// to ensure that notebook extensions not break before they update APIs.
|
||||
visibleNotebookEditors: vscode.NotebookEditor[];
|
||||
onDidChangeVisibleNotebookEditors: Event<vscode.NotebookEditor[]>;
|
||||
activeNotebookEditor: vscode.NotebookEditor | undefined;
|
||||
onDidChangeActiveNotebookEditor: Event<vscode.NotebookEditor | undefined>;
|
||||
onDidChangeNotebookEditorSelection: Event<vscode.NotebookEditorSelectionChangeEvent>;
|
||||
onDidChangeNotebookEditorVisibleRanges: Event<vscode.NotebookEditorVisibleRangesChangeEvent>;
|
||||
}) = {
|
||||
openNotebookDocument: (uriComponents, viewType) => {
|
||||
const notebook: typeof vscode.notebook = {
|
||||
openNotebookDocument: (uriComponents) => {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.openNotebookDocument(uriComponents, viewType);
|
||||
return extHostNotebook.openNotebookDocument(uriComponents);
|
||||
},
|
||||
get onDidOpenNotebookDocument(): Event<vscode.NotebookDocument> {
|
||||
checkProposedApiEnabled(extension);
|
||||
@@ -1041,18 +1047,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.notebookDocuments.map(d => d.notebookDocument);
|
||||
},
|
||||
get visibleNotebookEditors(): vscode.NotebookEditor[] {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.visibleNotebookEditors;
|
||||
},
|
||||
get onDidChangeVisibleNotebookEditors() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeVisibleNotebookEditors;
|
||||
},
|
||||
get onDidChangeActiveNotebookKernel() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeActiveNotebookKernel;
|
||||
},
|
||||
registerNotebookSerializer(viewType, serializer, options) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.registerNotebookSerializer(extension, viewType, serializer, options);
|
||||
},
|
||||
registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider, options?: {
|
||||
transientOutputs: boolean;
|
||||
transientMetadata: { [K in keyof vscode.NotebookCellMetadata]?: boolean }
|
||||
@@ -1068,14 +1070,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.createNotebookEditorDecorationType(options);
|
||||
},
|
||||
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.activeNotebookEditor;
|
||||
},
|
||||
onDidChangeActiveNotebookEditor(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeActiveNotebookEditor(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeNotebookDocumentMetadata(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookDocumentMetadata(listener, thisArgs, disposables);
|
||||
@@ -1084,22 +1078,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookCells(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeNotebookEditorSelection(listener, thisArgs?, disposables?) {
|
||||
onDidChangeCellExecutionState(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookEditorSelection(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeNotebookEditorVisibleRanges(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeNotebookEditorVisibleRanges(listener, thisArgs, disposables);
|
||||
return extHostNotebook.onDidChangeNotebookCellExecutionState(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeCellOutputs(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeCellOutputs(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeCellLanguage(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeCellLanguage(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeCellMetadata(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeCellMetadata(listener, thisArgs, disposables);
|
||||
@@ -1111,6 +1097,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
createCellStatusBarItem(cell: vscode.NotebookCell, alignment?: vscode.NotebookCellStatusBarAlignment, priority?: number): vscode.NotebookCellStatusBarItem {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.createNotebookCellStatusBarItemInternal(cell, alignment, priority);
|
||||
},
|
||||
createNotebookCellExecutionTask(uri: vscode.Uri, index: number, kernelId: string): vscode.NotebookCellExecutionTask | undefined {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.createNotebookCellExecution(uri, index, kernelId);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1140,7 +1130,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
CandidatePortSource: CandidatePortSource,
|
||||
CodeAction: extHostTypes.CodeAction,
|
||||
CodeActionKind: extHostTypes.CodeActionKind,
|
||||
CodeActionTrigger: extHostTypes.CodeActionTrigger,
|
||||
CodeActionTriggerKind: extHostTypes.CodeActionTriggerKind,
|
||||
CodeLens: extHostTypes.CodeLens,
|
||||
Color: extHostTypes.Color,
|
||||
ColorInformation: extHostTypes.ColorInformation,
|
||||
@@ -1194,6 +1184,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
MarkdownString: extHostTypes.MarkdownString,
|
||||
OverviewRulerLane: OverviewRulerLane,
|
||||
ParameterInformation: extHostTypes.ParameterInformation,
|
||||
PortAutoForwardAction: extHostTypes.PortAutoForwardAction,
|
||||
Position: extHostTypes.Position,
|
||||
ProcessExecution: extHostTypes.ProcessExecution,
|
||||
ProgressLocation: extHostTypes.ProgressLocation,
|
||||
@@ -1239,124 +1230,31 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
ViewColumn: extHostTypes.ViewColumn,
|
||||
WorkspaceEdit: extHostTypes.WorkspaceEdit,
|
||||
// proposed api types
|
||||
get InlineHint() {
|
||||
return extHostTypes.InlineHint;
|
||||
},
|
||||
get InlineHintKind() {
|
||||
return extHostTypes.InlineHintKind;
|
||||
},
|
||||
get RemoteAuthorityResolverError() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.RemoteAuthorityResolverError;
|
||||
},
|
||||
get ResolvedAuthority() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.ResolvedAuthority;
|
||||
},
|
||||
get SourceControlInputBoxValidationType() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.SourceControlInputBoxValidationType;
|
||||
},
|
||||
get ExtensionRuntime() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.ExtensionRuntime;
|
||||
},
|
||||
get TimelineItem() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TimelineItem;
|
||||
},
|
||||
get NotebookCellRange() {
|
||||
return extHostTypes.NotebookCellRange;
|
||||
},
|
||||
get NotebookCellKind() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellKind;
|
||||
},
|
||||
get NotebookCellRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellRunState;
|
||||
},
|
||||
get NotebookDocumentMetadata() {
|
||||
return extHostTypes.NotebookDocumentMetadata;
|
||||
},
|
||||
get NotebookCellMetadata() {
|
||||
return extHostTypes.NotebookCellMetadata;
|
||||
},
|
||||
get NotebookRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookRunState;
|
||||
},
|
||||
get NotebookCellStatusBarAlignment() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellStatusBarAlignment;
|
||||
},
|
||||
get NotebookEditorRevealType() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookEditorRevealType;
|
||||
},
|
||||
get NotebookCellOutput() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellOutput;
|
||||
},
|
||||
get NotebookCellOutputItem() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.NotebookCellOutputItem;
|
||||
},
|
||||
get LinkedEditingRanges() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.LinkedEditingRanges;
|
||||
},
|
||||
get TestRunState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestRunState;
|
||||
},
|
||||
get TestMessageSeverity() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.TestMessageSeverity;
|
||||
},
|
||||
get WorkspaceTrustState() {
|
||||
// checkProposedApiEnabled(extension);
|
||||
return extHostTypes.WorkspaceTrustState;
|
||||
}
|
||||
InlineHint: extHostTypes.InlineHint,
|
||||
InlineHintKind: extHostTypes.InlineHintKind,
|
||||
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
|
||||
ResolvedAuthority: extHostTypes.ResolvedAuthority,
|
||||
SourceControlInputBoxValidationType: extHostTypes.SourceControlInputBoxValidationType,
|
||||
ExtensionRuntime: extHostTypes.ExtensionRuntime,
|
||||
TimelineItem: extHostTypes.TimelineItem,
|
||||
NotebookCellRange: extHostTypes.NotebookCellRange,
|
||||
NotebookCellKind: extHostTypes.NotebookCellKind,
|
||||
NotebookCellExecutionState: extHostTypes.NotebookCellExecutionState,
|
||||
NotebookDocumentMetadata: extHostTypes.NotebookDocumentMetadata,
|
||||
NotebookCellMetadata: extHostTypes.NotebookCellMetadata,
|
||||
NotebookCellData: extHostTypes.NotebookCellData,
|
||||
NotebookData: extHostTypes.NotebookData,
|
||||
NotebookCellStatusBarAlignment: extHostTypes.NotebookCellStatusBarAlignment,
|
||||
NotebookEditorRevealType: extHostTypes.NotebookEditorRevealType,
|
||||
NotebookCellOutput: extHostTypes.NotebookCellOutput,
|
||||
NotebookCellOutputItem: extHostTypes.NotebookCellOutputItem,
|
||||
LinkedEditingRanges: extHostTypes.LinkedEditingRanges,
|
||||
TestItem: extHostTypes.TestItem,
|
||||
TestState: extHostTypes.TestState,
|
||||
TestResult: extHostTypes.TestResult,
|
||||
TestMessage: extHostTypes.TestMessage,
|
||||
TestMessageSeverity: extHostTypes.TestMessageSeverity,
|
||||
WorkspaceTrustState: extHostTypes.WorkspaceTrustState
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
class Extension<T> implements vscode.Extension<T> {
|
||||
|
||||
#extensionService: IExtHostExtensionService;
|
||||
#originExtensionId: ExtensionIdentifier;
|
||||
#identifier: ExtensionIdentifier;
|
||||
|
||||
readonly id: string;
|
||||
readonly extensionUri: URI;
|
||||
readonly extensionPath: string;
|
||||
readonly packageJSON: IExtensionDescription;
|
||||
readonly extensionKind: vscode.ExtensionKind;
|
||||
|
||||
constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: extHostTypes.ExtensionKind) {
|
||||
this.#extensionService = extensionService;
|
||||
this.#originExtensionId = originExtensionId;
|
||||
this.#identifier = description.identifier;
|
||||
this.id = description.identifier.value;
|
||||
this.extensionUri = description.extensionLocation;
|
||||
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
|
||||
this.packageJSON = description;
|
||||
this.extensionKind = kind;
|
||||
}
|
||||
|
||||
get isActive(): boolean {
|
||||
return this.#extensionService.isActivated(this.#identifier);
|
||||
}
|
||||
|
||||
get exports(): T {
|
||||
if (this.packageJSON.api === 'none') {
|
||||
return undefined!; // Strict nulloverride - Public api
|
||||
}
|
||||
return <T>this.#extensionService.getExtensionExports(this.#identifier);
|
||||
}
|
||||
|
||||
activate(): Thenable<T> {
|
||||
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { IExtHostWindow, ExtHostWindow } from 'vs/workbench/api/common/extHostWi
|
||||
import { IExtHostConsumerFileSystem, ExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
|
||||
import { IExtHostFileSystemInfo, ExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
|
||||
import { IExtHostSecretState, ExtHostSecretState } from 'vs/workbench/api/common/exHostSecretState';
|
||||
import { ExtHostTelemetry, IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry';
|
||||
|
||||
registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
|
||||
registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService);
|
||||
@@ -41,3 +42,4 @@ registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
|
||||
registerSingleton(IExtHostWindow, ExtHostWindow);
|
||||
registerSingleton(IExtHostWorkspace, ExtHostWorkspace);
|
||||
registerSingleton(IExtHostSecretState, ExtHostSecretState);
|
||||
registerSingleton(IExtHostTelemetry, ExtHostTelemetry);
|
||||
|
||||
@@ -47,20 +47,21 @@ import * as search from 'vs/workbench/services/search/common/search';
|
||||
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, ProvidedPortAttributes } from 'vs/platform/remote/common/tunnel';
|
||||
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEventDto, NotebookDataDto, IMainCellDto, INotebookDocumentFilter, TransientMetadata, INotebookCellStatusBarEntry, ICellRange, INotebookDecorationRenderOptions, INotebookExclusiveDocumentFilter, IOutputDto, TransientOptions, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { Dto } from 'vs/base/common/types';
|
||||
import { DebugConfigurationProviderTriggerKind, WorkspaceTrustState } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IExtensionIdWithVersion } from 'vs/platform/userDataSync/common/extensionsStorageSync';
|
||||
import { InternalTestItem, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithProvider, TestsDiff, ISerializedTestResults } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { InternalTestItem, ITestState, RunTestForProviderRequest, RunTestsRequest, TestIdWithSrc, TestsDiff, ISerializedTestResults } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { WorkspaceTrustRequestOptions, WorkspaceTrustStateChangeEvent } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { IShellLaunchConfig, ITerminalDimensions, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
|
||||
export interface IEnvironment {
|
||||
isExtensionDevelopmentDebug: boolean;
|
||||
@@ -457,7 +458,7 @@ export interface TerminalLaunchConfig {
|
||||
waitOnExit?: boolean;
|
||||
strictEnv?: boolean;
|
||||
hideFromUser?: boolean;
|
||||
isExtensionTerminal?: boolean;
|
||||
isExtensionCustomPtyTerminal?: boolean;
|
||||
isFeatureTerminal?: boolean;
|
||||
isExtensionOwnedTerminal?: boolean;
|
||||
}
|
||||
@@ -504,6 +505,8 @@ export interface BaseTransferQuickInput {
|
||||
|
||||
id: number;
|
||||
|
||||
title?: string;
|
||||
|
||||
type?: 'quickPick' | 'inputBox';
|
||||
|
||||
enabled?: boolean;
|
||||
@@ -558,6 +561,7 @@ export interface TransferInputBox extends BaseTransferQuickInput {
|
||||
}
|
||||
|
||||
export interface IInputBoxOptions {
|
||||
title?: string;
|
||||
value?: string;
|
||||
valueSelection?: [number, number];
|
||||
prompt?: string;
|
||||
@@ -592,11 +596,11 @@ export interface MainThreadTelemetryShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadEditorInsetsShape extends IDisposable {
|
||||
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: modes.IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
|
||||
$createEditorInset(handle: number, id: string, uri: UriComponents, line: number, height: number, options: IWebviewOptions, extensionId: ExtensionIdentifier, extensionLocation: UriComponents): Promise<void>;
|
||||
$disposeEditorInset(handle: number): void;
|
||||
|
||||
$setHtml(handle: number, value: string): void;
|
||||
$setOptions(handle: number, options: modes.IWebviewOptions): void;
|
||||
$setOptions(handle: number, options: IWebviewOptions): void;
|
||||
$postMessage(handle: number, value: any): Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -646,18 +650,45 @@ export enum WebviewEditorCapabilities {
|
||||
SupportsHotExit,
|
||||
}
|
||||
|
||||
export interface IWebviewPortMapping {
|
||||
readonly webviewPort: number;
|
||||
readonly extensionHostPort: number;
|
||||
}
|
||||
|
||||
export interface IWebviewOptions {
|
||||
readonly enableScripts?: boolean;
|
||||
readonly enableCommandUris?: boolean;
|
||||
readonly localResourceRoots?: ReadonlyArray<UriComponents>;
|
||||
readonly portMapping?: ReadonlyArray<IWebviewPortMapping>;
|
||||
}
|
||||
|
||||
export interface IWebviewPanelOptions {
|
||||
readonly enableFindWidget?: boolean;
|
||||
readonly retainContextWhenHidden?: boolean;
|
||||
}
|
||||
|
||||
export interface CustomTextEditorCapabilities {
|
||||
readonly supportsMove?: boolean;
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewsShape extends IDisposable {
|
||||
$setHtml(handle: WebviewHandle, value: string): void;
|
||||
$setOptions(handle: WebviewHandle, options: modes.IWebviewOptions): void;
|
||||
$setOptions(handle: WebviewHandle, options: IWebviewOptions): void;
|
||||
$postMessage(handle: WebviewHandle, value: any): Promise<boolean>
|
||||
}
|
||||
|
||||
export interface MainThreadWebviewPanelsShape extends IDisposable {
|
||||
$createWebviewPanel(extension: WebviewExtensionDescription, handle: WebviewHandle, viewType: string, title: string, showOptions: WebviewPanelShowOptions, options: modes.IWebviewPanelOptions & modes.IWebviewOptions): void;
|
||||
$createWebviewPanel(
|
||||
extension: WebviewExtensionDescription,
|
||||
handle: WebviewHandle,
|
||||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
},
|
||||
showOptions: WebviewPanelShowOptions,
|
||||
): void;
|
||||
$disposeWebview(handle: WebviewHandle): void;
|
||||
$reveal(handle: WebviewHandle, showOptions: WebviewPanelShowOptions): void;
|
||||
$setTitle(handle: WebviewHandle, value: string): void;
|
||||
@@ -668,8 +699,8 @@ export interface MainThreadWebviewPanelsShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadCustomEditorsShape extends IDisposable {
|
||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
|
||||
$registerTextEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, capabilities: CustomTextEditorCapabilities): void;
|
||||
$registerCustomEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: IWebviewPanelOptions, supportsMultipleEditorsPerDocument: boolean): void;
|
||||
$unregisterEditorProvider(viewType: string): void;
|
||||
|
||||
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
|
||||
@@ -702,12 +733,33 @@ export interface ExtHostWebviewsShape {
|
||||
export interface ExtHostWebviewPanelsShape {
|
||||
$onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void;
|
||||
$onDidDisposeWebviewPanel(handle: WebviewHandle): Promise<void>;
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewHandle, viewType: string, title: string, state: any, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$deserializeWebviewPanel(
|
||||
newWebviewHandle: WebviewHandle,
|
||||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
state: any;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostCustomEditorsShape {
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewHandle, viewType: string, title: string, position: EditorGroupColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, cancellation: CancellationToken): Promise<void>;
|
||||
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
|
||||
$resolveWebviewEditor(
|
||||
resource: UriComponents,
|
||||
newWebviewHandle: WebviewHandle,
|
||||
viewType: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: IWebviewOptions;
|
||||
panelOptions: IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
cancellation: CancellationToken
|
||||
): Promise<void>;
|
||||
$createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, cancellation: CancellationToken): Promise<{ editable: boolean }>;
|
||||
$disposeCustomDocument(resource: UriComponents, viewType: string): Promise<void>;
|
||||
|
||||
$undo(resource: UriComponents, viewType: string, editId: number, isDirty: boolean): Promise<void>;
|
||||
@@ -788,14 +840,19 @@ export interface MainThreadNotebookShape extends IDisposable {
|
||||
}): Promise<void>;
|
||||
$updateNotebookProviderOptions(viewType: string, options?: { transientOutputs: boolean; transientMetadata: TransientMetadata; }): Promise<void>;
|
||||
$unregisterNotebookProvider(viewType: string): Promise<void>;
|
||||
|
||||
$registerNotebookSerializer(handle: number, extension: NotebookExtensionDescription, viewType: string, options: TransientOptions): void;
|
||||
$unregisterNotebookSerializer(handle: number): void;
|
||||
|
||||
$registerNotebookKernelProvider(extension: NotebookExtensionDescription, handle: number, documentFilter: INotebookDocumentFilter): Promise<void>;
|
||||
$unregisterNotebookKernelProvider(handle: number): Promise<void>;
|
||||
$onNotebookKernelChange(handle: number, uri: UriComponents | undefined): void;
|
||||
$trySaveDocument(uri: UriComponents): Promise<boolean>;
|
||||
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[]): Promise<boolean>;
|
||||
$applyEdits(resource: UriComponents, edits: IImmediateCellEditOperation[], computeUndoRedo?: boolean): Promise<void>;
|
||||
$postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean>;
|
||||
$setStatusBarEntry(id: number, statusBarEntry: INotebookCellStatusBarEntryDto): Promise<void>;
|
||||
$tryOpenDocument(uriComponents: UriComponents, viewType?: string): Promise<URI>;
|
||||
$tryOpenDocument(uriComponents: UriComponents): Promise<UriComponents>;
|
||||
$tryShowNotebookDocument(uriComponents: UriComponents, viewType: string, options: INotebookDocumentShowOptions): Promise<string>;
|
||||
$tryRevealRange(id: string, range: ICellRange, revealType: NotebookEditorRevealType): Promise<void>;
|
||||
$registerNotebookEditorDecorationType(key: string, options: INotebookDecorationRenderOptions): void;
|
||||
@@ -834,7 +891,7 @@ export interface MainThreadWorkspaceShape extends IDisposable {
|
||||
$saveAll(includeUntitled?: boolean): Promise<boolean>;
|
||||
$updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string; }[]): Promise<void>;
|
||||
$resolveProxy(url: string): Promise<string | undefined>;
|
||||
$requireWorkspaceTrust(modal?: boolean): Promise<WorkspaceTrustState>
|
||||
$requireWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState>;
|
||||
}
|
||||
|
||||
export interface IFileChangeDto {
|
||||
@@ -890,7 +947,6 @@ export interface MainThreadExtensionServiceShape extends IDisposable {
|
||||
$onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
|
||||
$onExtensionActivationError(extensionId: ExtensionIdentifier, error: ExtensionActivationError): Promise<void>;
|
||||
$onExtensionRuntimeError(extensionId: ExtensionIdentifier, error: SerializedError): void;
|
||||
$onExtensionHostExit(code: number): Promise<void>;
|
||||
$setPerformanceMarks(marks: performance.PerformanceMark[]): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -1000,6 +1056,11 @@ export enum CandidatePortSource {
|
||||
Output = 2
|
||||
}
|
||||
|
||||
export interface PortAttributesProviderSelector {
|
||||
pid?: number;
|
||||
portRange?: [number, number];
|
||||
}
|
||||
|
||||
export interface MainThreadTunnelServiceShape extends IDisposable {
|
||||
$openTunnel(tunnelOptions: TunnelOptions, source: string | undefined): Promise<TunnelDto | undefined>;
|
||||
$closeTunnel(remote: { host: string, port: number }): Promise<void>;
|
||||
@@ -1007,8 +1068,10 @@ export interface MainThreadTunnelServiceShape extends IDisposable {
|
||||
$setTunnelProvider(features: TunnelProviderFeatures): Promise<void>;
|
||||
$setRemoteTunnelService(processId: number): Promise<void>;
|
||||
$setCandidateFilter(): Promise<void>;
|
||||
$onFoundNewCandidates(candidates: { host: string, port: number, detail: string }[]): Promise<void>;
|
||||
$onFoundNewCandidates(candidates: CandidatePort[]): Promise<void>;
|
||||
$setCandidatePortSource(source: CandidatePortSource): Promise<void>;
|
||||
$registerPortsAttributesProvider(selector: PortAttributesProviderSelector, providerHandle: number): Promise<void>;
|
||||
$unregisterPortsAttributesProvider(providerHandle: number): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadTimelineShape extends IDisposable {
|
||||
@@ -1046,7 +1109,7 @@ export interface IModelAddedData {
|
||||
isDirty: boolean;
|
||||
}
|
||||
export interface ExtHostDocumentsShape {
|
||||
$acceptModelModeChanged(strURL: UriComponents, oldModeId: string, newModeId: string): void;
|
||||
$acceptModelModeChanged(strURL: UriComponents, newModeId: string): void;
|
||||
$acceptModelSaved(strURL: UriComponents): void;
|
||||
$acceptDirtyStateChanged(strURL: UriComponents, isDirty: boolean): void;
|
||||
$acceptModelChanged(strURL: UriComponents, e: IModelChangedEvent, isDirty: boolean): void;
|
||||
@@ -1173,6 +1236,8 @@ export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAut
|
||||
export interface ExtHostExtensionServiceShape {
|
||||
$resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
|
||||
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
|
||||
$extensionTestsExecute(): Promise<number>;
|
||||
$extensionTestsExit(code: number): Promise<void>;
|
||||
$activateByEvent(activationEvent: string, activationKind: ActivationKind): Promise<void>;
|
||||
$activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean>;
|
||||
$setRemoteEnvironment(env: { [key: string]: string | null; }): Promise<void>;
|
||||
@@ -1383,9 +1448,6 @@ export interface IWorkspaceCellEditDto {
|
||||
|
||||
export interface IWorkspaceEditDto {
|
||||
edits: Array<IWorkspaceFileEditDto | IWorkspaceTextEditDto | IWorkspaceCellEditDto>;
|
||||
|
||||
// todo@jrieken reject should go into rename
|
||||
rejectReason?: string;
|
||||
}
|
||||
|
||||
export function reviveWorkspaceEditDto(data: IWorkspaceEditDto | undefined): modes.WorkspaceEdit {
|
||||
@@ -1478,6 +1540,7 @@ export interface ILinkedEditingRangesDto {
|
||||
}
|
||||
|
||||
export interface IInlineValueContextDto {
|
||||
frameId: number;
|
||||
stoppedLocation: IRange;
|
||||
}
|
||||
|
||||
@@ -1505,7 +1568,7 @@ export interface ExtHostLanguageFeaturesShape {
|
||||
$provideWorkspaceSymbols(handle: number, search: string, token: CancellationToken): Promise<IWorkspaceSymbolsDto>;
|
||||
$resolveWorkspaceSymbol(handle: number, symbol: IWorkspaceSymbolDto, token: CancellationToken): Promise<IWorkspaceSymbolDto | undefined>;
|
||||
$releaseWorkspaceSymbols(handle: number, id: number): void;
|
||||
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto | undefined>;
|
||||
$provideRenameEdits(handle: number, resource: UriComponents, position: IPosition, newName: string, token: CancellationToken): Promise<IWorkspaceEditDto & { rejectReason?: string } | undefined>;
|
||||
$resolveRenameLocation(handle: number, resource: UriComponents, position: IPosition, token: CancellationToken): Promise<modes.RenameLocation | undefined>;
|
||||
$provideDocumentSemanticTokens(handle: number, resource: UriComponents, previousResultId: number, token: CancellationToken): Promise<VSBuffer | null>;
|
||||
$releaseDocumentSemanticTokens(handle: number, semanticColoringResultId: number): void;
|
||||
@@ -1541,6 +1604,11 @@ export interface ExtHostQuickOpenShape {
|
||||
$onDidHide(sessionId: number): void;
|
||||
}
|
||||
|
||||
export interface ExtHostTelemetryShape {
|
||||
$initializeTelemetryEnabled(enabled: boolean): void;
|
||||
$onDidChangeTelemetryEnabled(enabled: boolean): void;
|
||||
}
|
||||
|
||||
export interface IShellLaunchConfigDto {
|
||||
name?: string;
|
||||
executable?: string;
|
||||
@@ -1548,12 +1616,6 @@ export interface IShellLaunchConfigDto {
|
||||
cwd?: string | UriComponents;
|
||||
env?: { [key: string]: string | null; };
|
||||
hideFromUser?: boolean;
|
||||
flowControl?: boolean;
|
||||
}
|
||||
|
||||
export interface IShellDefinitionDto {
|
||||
label: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
export interface IShellAndArgsDto {
|
||||
@@ -1586,7 +1648,6 @@ export interface ExtHostTerminalServiceShape {
|
||||
$acceptTerminalTitleChange(id: number, name: string): void;
|
||||
$acceptTerminalDimensions(id: number, cols: number, rows: number): void;
|
||||
$acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void;
|
||||
$spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
|
||||
$startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<ITerminalLaunchError | undefined>;
|
||||
$acceptProcessAckDataEvent(id: number, charCount: number): void;
|
||||
$acceptProcessInput(id: number, data: string): void;
|
||||
@@ -1596,7 +1657,8 @@ export interface ExtHostTerminalServiceShape {
|
||||
$acceptProcessRequestCwd(id: number): void;
|
||||
$acceptProcessRequestLatency(id: number): number;
|
||||
$acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||
$getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||
// TODO: Change quickLaunchOnly to "includeAutoDetected" or something similar
|
||||
$getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]>;
|
||||
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
$provideLinks(id: number, line: string): Promise<ITerminalLinkDto[]>;
|
||||
$activateLink(id: number, linkId: number): void;
|
||||
@@ -1644,6 +1706,7 @@ export interface IDataBreakpointDto extends IBreakpointDto {
|
||||
canPersist: boolean;
|
||||
label: string;
|
||||
accessTypes?: DebugProtocol.DataBreakpointAccessType[];
|
||||
accessType: DebugProtocol.DataBreakpointAccessType;
|
||||
}
|
||||
|
||||
export interface ISourceBreakpointDto extends IBreakpointDto {
|
||||
@@ -1751,12 +1814,12 @@ export interface INotebookVisibleRangesEvent {
|
||||
}
|
||||
|
||||
export interface INotebookEditorPropertiesChangeData {
|
||||
visibleRanges: INotebookVisibleRangesEvent | null;
|
||||
selections: INotebookSelectionChangeEvent | null;
|
||||
visibleRanges?: INotebookVisibleRangesEvent;
|
||||
selections?: INotebookSelectionChangeEvent;
|
||||
}
|
||||
|
||||
export interface INotebookDocumentPropertiesChangeData {
|
||||
metadata: NotebookDocumentMetadata | null;
|
||||
metadata?: NotebookDocumentMetadata;
|
||||
}
|
||||
|
||||
export interface INotebookModelAddedData {
|
||||
@@ -1765,7 +1828,6 @@ export interface INotebookModelAddedData {
|
||||
cells: IMainCellDto[],
|
||||
viewType: string;
|
||||
metadata?: NotebookDocumentMetadata;
|
||||
contentOptions: { transientOutputs: boolean; transientMetadata: TransientMetadata; }
|
||||
}
|
||||
|
||||
export interface INotebookEditorAddData {
|
||||
@@ -1773,6 +1835,7 @@ export interface INotebookEditorAddData {
|
||||
documentUri: UriComponents;
|
||||
selections: ICellRange[];
|
||||
visibleRanges: ICellRange[];
|
||||
viewColumn?: number
|
||||
}
|
||||
|
||||
export interface INotebookDocumentsAndEditorsDelta {
|
||||
@@ -1796,6 +1859,7 @@ export interface INotebookKernelInfoDto2 {
|
||||
isPreferred?: boolean;
|
||||
preloads?: UriComponents[];
|
||||
supportedLanguages?: string[]
|
||||
implementsInterrupt?: boolean;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookShape {
|
||||
@@ -1803,13 +1867,18 @@ export interface ExtHostNotebookShape {
|
||||
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelFriendlyId: string | undefined }): void;
|
||||
$provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise<INotebookKernelInfoDto2[]>;
|
||||
$resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void>;
|
||||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void>;
|
||||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellRanges: ICellRange[]): Promise<void>;
|
||||
$cancelNotebookCellExecution(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void>;
|
||||
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
|
||||
$openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto>;
|
||||
|
||||
$openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<NotebookDataDto>;
|
||||
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$backupNotebook(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string>;
|
||||
|
||||
$dataToNotebook(handle: number, data: VSBuffer): Promise<NotebookDataDto>;
|
||||
$notebookToData(handle: number, data: NotebookDataDto): Promise<VSBuffer>;
|
||||
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEventDto, isDirty: boolean): void;
|
||||
$acceptDirtyStateChanged(uriComponents: UriComponents, isDirty: boolean): void;
|
||||
$acceptModelSaved(uriComponents: UriComponents): void;
|
||||
@@ -1835,6 +1904,7 @@ export interface ExtHostTunnelServiceShape {
|
||||
$onDidTunnelsChange(): Promise<void>;
|
||||
$registerCandidateFinder(enable: boolean): Promise<void>;
|
||||
$applyCandidateFilter(candidates: CandidatePort[]): Promise<CandidatePort[]>;
|
||||
$providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: CancellationToken): Promise<ProvidedPortAttributes[]>;
|
||||
}
|
||||
|
||||
export interface ExtHostTimelineShape {
|
||||
@@ -1850,9 +1920,10 @@ export interface ExtHostTestingShape {
|
||||
$runTestsForProvider(req: RunTestForProviderRequest, token: CancellationToken): Promise<void>;
|
||||
$subscribeToTests(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
$unsubscribeFromTests(resource: ExtHostTestingResource, uri: UriComponents): void;
|
||||
$lookupTest(test: TestIdWithProvider): Promise<InternalTestItem | undefined>;
|
||||
$lookupTest(test: TestIdWithSrc): Promise<InternalTestItem | undefined>;
|
||||
$acceptDiff(resource: ExtHostTestingResource, uri: UriComponents, diff: TestsDiff): void;
|
||||
$publishTestResults(results: ISerializedTestResults[]): void;
|
||||
$expandTest(src: TestIdWithSrc, levels: number): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadTestingShape {
|
||||
@@ -1864,7 +1935,6 @@ export interface MainThreadTestingShape {
|
||||
$updateTestStateInRun(runId: string, testId: string, state: ITestState): void;
|
||||
$runTests(req: RunTestsRequest, token: CancellationToken): Promise<string>;
|
||||
$publishExtensionProvidedResults(results: ISerializedTestResults, persist: boolean): void;
|
||||
$retireTest(extId: string): void;
|
||||
}
|
||||
|
||||
// --- proxy identifiers
|
||||
@@ -1968,4 +2038,5 @@ export const ExtHostContext = {
|
||||
ExtHostAuthentication: createMainId<ExtHostAuthenticationShape>('ExtHostAuthentication'),
|
||||
ExtHostTimeline: createMainId<ExtHostTimelineShape>('ExtHostTimeline'),
|
||||
ExtHostTesting: createMainId<ExtHostTestingShape>('ExtHostTesting'),
|
||||
ExtHostTelemetry: createMainId<ExtHostTelemetryShape>('ExtHostTelemetry'),
|
||||
};
|
||||
|
||||
@@ -162,7 +162,7 @@ const newCommands: ApiCommand[] = [
|
||||
new ApiCommand(
|
||||
'vscode.executeDocumentRenameProvider', '_executeDocumentRenameProvider', 'Execute rename provider.',
|
||||
[ApiCommandArgument.Uri, ApiCommandArgument.Position, ApiCommandArgument.String.with('newName', 'The new symbol name')],
|
||||
new ApiCommandResult<IWorkspaceEditDto, types.WorkspaceEdit | undefined>('A promise that resolves to a WorkspaceEdit.', value => {
|
||||
new ApiCommandResult<IWorkspaceEditDto & { rejectReason?: string }, types.WorkspaceEdit | undefined>('A promise that resolves to a WorkspaceEdit.', value => {
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { MainContext, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -17,13 +16,12 @@ export class ExtHostBulkEdits {
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
private readonly _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private readonly _extHostNotebooks: ExtHostNotebookController,
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadBulkEdits);
|
||||
}
|
||||
|
||||
applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise<boolean> {
|
||||
const dto = WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors, this._extHostNotebooks);
|
||||
const dto = WorkspaceEdit.from(edit, this._extHostDocumentsAndEditors);
|
||||
return this._proxy.$tryApplyWorkspaceEdit(dto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
@@ -209,7 +209,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
}));
|
||||
}
|
||||
|
||||
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, cancellation: CancellationToken) {
|
||||
async $createCustomDocument(resource: UriComponents, viewType: string, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, cancellation: CancellationToken) {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
if (!entry) {
|
||||
throw new Error(`No provider found for '${viewType}'`);
|
||||
@@ -220,7 +220,7 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
}
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
|
||||
const document = await entry.provider.openCustomDocument(revivedResource, { backupId, untitledDocumentData: untitledDocumentData?.buffer }, cancellation);
|
||||
|
||||
let storageRoot: URI | undefined;
|
||||
if (this.supportEditing(entry.provider) && this._extensionStoragePaths) {
|
||||
@@ -251,9 +251,12 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
resource: UriComponents,
|
||||
handle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
initData: {
|
||||
title: string;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn,
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions,
|
||||
cancellation: CancellationToken,
|
||||
): Promise<void> {
|
||||
const entry = this._editorProviders.get(viewType);
|
||||
@@ -263,8 +266,8 @@ export class ExtHostCustomEditors implements extHostProtocol.ExtHostCustomEditor
|
||||
|
||||
const viewColumn = typeConverters.ViewColumn.to(position);
|
||||
|
||||
const webview = this._extHostWebview.createNewWebview(handle, options, entry.extension);
|
||||
const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview);
|
||||
const webview = this._extHostWebview.createNewWebview(handle, initData.webviewOptions, entry.extension);
|
||||
const panel = this._extHostWebviewPanels.createNewWebviewPanel(handle, viewType, initData.title, viewColumn, initData.panelOptions, webview);
|
||||
|
||||
const revivedResource = URI.revive(resource);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import type * as vscode from 'vscode';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import * as process from 'vs/base/common/process';
|
||||
|
||||
export const IExtHostDebugService = createDecorator<IExtHostDebugService>('IExtHostDebugService');
|
||||
|
||||
@@ -930,7 +930,7 @@ export class ExtHostDebugConsole {
|
||||
|
||||
export class ExtHostVariableResolverService extends AbstractVariableResolverService {
|
||||
|
||||
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment, workspaceService?: IExtHostWorkspace) {
|
||||
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, workspaceService?: IExtHostWorkspace) {
|
||||
super({
|
||||
getFolderUri: (folderName: string): URI | undefined => {
|
||||
const found = folders.filter(f => f.name === folderName);
|
||||
@@ -946,10 +946,10 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
return configurationService.getConfiguration(undefined, folderUri).get<string>(section);
|
||||
},
|
||||
getAppRoot: (): string | undefined => {
|
||||
return env ? env['VSCODE_CWD'] : undefined;
|
||||
return process.cwd();
|
||||
},
|
||||
getExecPath: (): string | undefined => {
|
||||
return env ? env['VSCODE_EXEC_PATH'] : undefined;
|
||||
return process.env['VSCODE_EXEC_PATH'];
|
||||
},
|
||||
getFilePath: (): string | undefined => {
|
||||
if (editorService) {
|
||||
@@ -990,7 +990,7 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}, undefined, env);
|
||||
}, undefined, process.env);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import type * as vscode from 'vscode';
|
||||
import { MainContext, MainThreadDiagnosticsShape, ExtHostDiagnosticsShape, IMainContext } from './extHost.protocol';
|
||||
import { DiagnosticSeverity } from './extHostTypes';
|
||||
import * as converter from './extHostTypeConverters';
|
||||
import { mergeSort } from 'vs/base/common/arrays';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
@@ -78,7 +77,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
|
||||
let lastUri: vscode.Uri | undefined;
|
||||
|
||||
// ensure stable-sort
|
||||
first = mergeSort([...first], DiagnosticCollection._compareIndexedTuplesByUri);
|
||||
first = [...first].sort(DiagnosticCollection._compareIndexedTuplesByUri);
|
||||
|
||||
for (const tuple of first) {
|
||||
const [uri, diagnostics] = tuple;
|
||||
|
||||
@@ -102,7 +102,7 @@ export class ExtHostDocuments implements ExtHostDocumentsShape {
|
||||
return this._proxy.$tryCreateDocument(options).then(data => URI.revive(data));
|
||||
}
|
||||
|
||||
public $acceptModelModeChanged(uriComponents: UriComponents, oldModeId: string, newModeId: string): void {
|
||||
public $acceptModelModeChanged(uriComponents: UriComponents, newModeId: string): void {
|
||||
const uri = URI.revive(uriComponents);
|
||||
const data = this._documentsAndEditors.getDocument(uri);
|
||||
if (!data) {
|
||||
|
||||
@@ -25,7 +25,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { ExtensionGlobalMemento, ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
|
||||
import { RemoteAuthorityResolverError, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { RemoteAuthorityResolverError, ExtensionKind, ExtensionMode, ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
@@ -95,6 +95,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
private readonly _almostReadyToRunExtensions: Barrier;
|
||||
private readonly _readyToStartExtensionHost: Barrier;
|
||||
private readonly _readyToRunExtensions: Barrier;
|
||||
private readonly _eagerExtensionsActivated: Barrier;
|
||||
|
||||
protected readonly _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _storage: ExtHostStorage;
|
||||
private readonly _secretState: ExtHostSecretState;
|
||||
@@ -140,6 +142,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
this._almostReadyToRunExtensions = new Barrier();
|
||||
this._readyToStartExtensionHost = new Barrier();
|
||||
this._readyToRunExtensions = new Barrier();
|
||||
this._eagerExtensionsActivated = new Barrier();
|
||||
this._registry = new ExtensionDescriptionRegistry(this._initData.extensions);
|
||||
this._storage = new ExtHostStorage(this._extHostContext);
|
||||
this._secretState = new ExtHostSecretState(this._extHostContext);
|
||||
@@ -388,7 +391,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
const extensionMode = extensionDescription.isUnderDevelopment
|
||||
? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development)
|
||||
: ExtensionMode.Production;
|
||||
const installAge = Date.now() - new Date(this._initData.telemetryInfo.firstSessionDate).getTime();
|
||||
const extensionKind = this._initData.remote.isRemote ? ExtensionKind.Workspace : ExtensionKind.UI;
|
||||
|
||||
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
|
||||
|
||||
@@ -398,6 +401,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
this._storagePath.whenReady
|
||||
]).then(() => {
|
||||
const that = this;
|
||||
let extension: vscode.Extension<any> | undefined;
|
||||
|
||||
return Object.freeze<vscode.ExtensionContext>({
|
||||
globalState,
|
||||
workspaceState,
|
||||
@@ -413,22 +418,16 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
get storageUri() { return that._storagePath.workspaceValue(extensionDescription); },
|
||||
get globalStorageUri() { return that._storagePath.globalValue(extensionDescription); },
|
||||
get extensionMode() { return extensionMode; },
|
||||
get extensionId() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return extensionDescription.identifier.value;
|
||||
},
|
||||
get extensionVersion() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return extensionDescription.version;
|
||||
get extension() {
|
||||
if (extension === undefined) {
|
||||
extension = new Extension(that, extensionDescription.identifier, extensionDescription, extensionKind);
|
||||
}
|
||||
return extension;
|
||||
},
|
||||
get extensionRuntime() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return that.extensionRuntime;
|
||||
},
|
||||
get isNewInstall() {
|
||||
checkProposedApiEnabled(extensionDescription);
|
||||
return isNaN(installAge) ? false : installAge < 1000 * 60 * 60 * 24; // installAge is less than a day;
|
||||
},
|
||||
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); }
|
||||
});
|
||||
});
|
||||
@@ -550,84 +549,61 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
);
|
||||
}
|
||||
|
||||
private _handleExtensionTests(): Promise<void> {
|
||||
return this._doHandleExtensionTests().then(undefined, error => {
|
||||
public async $extensionTestsExecute(): Promise<number> {
|
||||
await this._eagerExtensionsActivated.wait();
|
||||
try {
|
||||
return this._doHandleExtensionTests();
|
||||
} catch (error) {
|
||||
console.error(error); // ensure any error message makes it onto the console
|
||||
|
||||
return Promise.reject(error);
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private async _doHandleExtensionTests(): Promise<void> {
|
||||
private async _doHandleExtensionTests(): Promise<number> {
|
||||
const { extensionDevelopmentLocationURI, extensionTestsLocationURI } = this._initData.environment;
|
||||
if (!(extensionDevelopmentLocationURI && extensionTestsLocationURI && extensionTestsLocationURI.scheme === Schemas.file)) {
|
||||
return Promise.resolve(undefined);
|
||||
if (!extensionDevelopmentLocationURI || !extensionTestsLocationURI || extensionTestsLocationURI.scheme !== Schemas.file) {
|
||||
throw new Error(nls.localize('extensionTestError1', "Cannot load test runner."));
|
||||
}
|
||||
|
||||
const extensionTestsPath = originalFSPath(extensionTestsLocationURI);
|
||||
|
||||
// Require the test runner via node require from the provided path
|
||||
let testRunner: ITestRunner | INewTestRunner | undefined;
|
||||
let requireError: Error | undefined;
|
||||
try {
|
||||
testRunner = await this._loadCommonJSModule(null, URI.file(extensionTestsPath), new ExtensionActivationTimesBuilder(false));
|
||||
} catch (error) {
|
||||
requireError = error;
|
||||
const testRunner: ITestRunner | INewTestRunner | undefined = await this._loadCommonJSModule(null, extensionTestsLocationURI, new ExtensionActivationTimesBuilder(false));
|
||||
|
||||
if (!testRunner || typeof testRunner.run !== 'function') {
|
||||
throw new Error(nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath));
|
||||
}
|
||||
|
||||
// Execute the runner if it follows the old `run` spec
|
||||
if (testRunner && typeof testRunner.run === 'function') {
|
||||
return new Promise<void>((c, e) => {
|
||||
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
|
||||
if (error) {
|
||||
e(error.toString());
|
||||
} else {
|
||||
c(undefined);
|
||||
}
|
||||
|
||||
// after tests have run, we shutdown the host
|
||||
this._testRunnerExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
|
||||
};
|
||||
|
||||
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
|
||||
|
||||
// Using the new API `run(): Promise<void>`
|
||||
if (runResult && runResult.then) {
|
||||
runResult
|
||||
.then(() => {
|
||||
c();
|
||||
this._testRunnerExit(0);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
e(err.toString());
|
||||
this._testRunnerExit(1);
|
||||
});
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
const oldTestRunnerCallback = (error: Error, failures: number | undefined) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve((typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Otherwise make sure to shutdown anyway even in case of an error
|
||||
else {
|
||||
this._testRunnerExit(1 /* ERROR */);
|
||||
}
|
||||
const runResult = testRunner.run(extensionTestsPath, oldTestRunnerCallback);
|
||||
|
||||
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
|
||||
// Using the new API `run(): Promise<void>`
|
||||
if (runResult && runResult.then) {
|
||||
runResult
|
||||
.then(() => {
|
||||
resolve(0);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
reject(err.toString());
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _testRunnerExit(code: number): void {
|
||||
public async $extensionTestsExit(code: number): Promise<void> {
|
||||
this._logService.info(`extension host terminating: test runner requested exit with code ${code}`);
|
||||
this._logService.info(`exiting with code ${code}`);
|
||||
this._logService.flush();
|
||||
|
||||
// wait at most 5000ms for the renderer to confirm our exit request and for the renderer socket to drain
|
||||
// (this is to ensure all outstanding messages reach the renderer)
|
||||
const exitPromise = this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
|
||||
const drainPromise = this._extHostContext.drain();
|
||||
Promise.race([Promise.all([exitPromise, drainPromise]), timeout(5000)]).then(() => {
|
||||
this._logService.info(`exiting with code ${code}`);
|
||||
this._logService.flush();
|
||||
|
||||
this._hostUtils.exit(code);
|
||||
});
|
||||
this._hostUtils.exit(code);
|
||||
}
|
||||
|
||||
private _startExtensionHost(): Promise<void> {
|
||||
@@ -639,8 +615,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
return this._readyToStartExtensionHost.wait()
|
||||
.then(() => this._readyToRunExtensions.open())
|
||||
.then(() => this._handleEagerExtensions())
|
||||
.then(() => this._handleExtensionTests())
|
||||
.then(() => {
|
||||
this._eagerExtensionsActivated.open();
|
||||
this._logService.info(`eager extensions activated`);
|
||||
});
|
||||
}
|
||||
@@ -840,3 +816,42 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic
|
||||
onDidChangeRemoteConnectionData: Event<void>;
|
||||
getRemoteConnectionData(): IRemoteConnectionData | null;
|
||||
}
|
||||
|
||||
export class Extension<T> implements vscode.Extension<T> {
|
||||
|
||||
#extensionService: IExtHostExtensionService;
|
||||
#originExtensionId: ExtensionIdentifier;
|
||||
#identifier: ExtensionIdentifier;
|
||||
|
||||
readonly id: string;
|
||||
readonly extensionUri: URI;
|
||||
readonly extensionPath: string;
|
||||
readonly packageJSON: IExtensionDescription;
|
||||
readonly extensionKind: vscode.ExtensionKind;
|
||||
|
||||
constructor(extensionService: IExtHostExtensionService, originExtensionId: ExtensionIdentifier, description: IExtensionDescription, kind: ExtensionKind) {
|
||||
this.#extensionService = extensionService;
|
||||
this.#originExtensionId = originExtensionId;
|
||||
this.#identifier = description.identifier;
|
||||
this.id = description.identifier.value;
|
||||
this.extensionUri = description.extensionLocation;
|
||||
this.extensionPath = path.normalize(originalFSPath(description.extensionLocation));
|
||||
this.packageJSON = description;
|
||||
this.extensionKind = kind;
|
||||
}
|
||||
|
||||
get isActive(): boolean {
|
||||
return this.#extensionService.isActivated(this.#identifier);
|
||||
}
|
||||
|
||||
get exports(): T {
|
||||
if (this.packageJSON.api === 'none') {
|
||||
return undefined!; // Strict nulloverride - Public api
|
||||
}
|
||||
return <T>this.#extensionService.getExtensionExports(this.#identifier);
|
||||
}
|
||||
|
||||
activate(): Thenable<T> {
|
||||
return this.#extensionService.activateByIdWithErrors(this.#identifier, { startup: false, extensionId: this.#originExtensionId, activationEvent: 'api' }).then(() => this.exports);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,7 +412,8 @@ class CodeActionAdapter {
|
||||
|
||||
const codeActionContext: vscode.CodeActionContext = {
|
||||
diagnostics: allDiagnostics,
|
||||
only: context.only ? new CodeActionKind(context.only) : undefined
|
||||
only: context.only ? new CodeActionKind(context.only) : undefined,
|
||||
triggerKind: typeConvert.CodeActionTriggerKind.to(context.trigger),
|
||||
};
|
||||
|
||||
return asPromise(() => this._provider.provideCodeActions(doc, ran, codeActionContext, token)).then((commandsOrActions): extHostProtocol.ICodeActionListDto | undefined => {
|
||||
|
||||
@@ -5,11 +5,11 @@
|
||||
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorPropertiesChangeData, INotebookKernelInfoDto2, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostNotebookShape, ICommandDto, IMainContext, IModelAddedData, INotebookDocumentPropertiesChangeData, INotebookDocumentsAndEditorsDelta, INotebookDocumentShowOptions, INotebookEditorAddData, INotebookEditorPropertiesChangeData, INotebookKernelInfoDto2, MainContext, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
@@ -17,7 +17,7 @@ import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePa
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import { CellStatusbarAlignment, CellUri, ICellRange, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, CellStatusbarAlignment, CellUri, ICellRange, INotebookCellStatusBarEntry, INotebookExclusiveDocumentFilter, NotebookCellMetadata, NotebookCellExecutionState, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookDataDto, NullablePartialNotebookCellMetadata, IImmediateCellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { ExtHostCell, ExtHostNotebookDocument } from './extHostNotebookDocument';
|
||||
@@ -25,7 +25,9 @@ import { ExtHostNotebookEditor } from './extHostNotebookEditor';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
|
||||
class ExtHostWebviewCommWrapper extends Disposable {
|
||||
private readonly _onDidReceiveDocumentMessage = new Emitter<any>();
|
||||
@@ -123,7 +125,8 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
detail: kernel.detail,
|
||||
isPreferred: kernel.isPreferred,
|
||||
preloads: kernel.preloads,
|
||||
supportedLanguages: kernel.supportedLanguages
|
||||
supportedLanguages: kernel.supportedLanguages,
|
||||
implementsInterrupt: !!kernel.interrupt
|
||||
};
|
||||
});
|
||||
|
||||
@@ -149,42 +152,25 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
|
||||
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cellRange: ICellRange[]): Promise<void> {
|
||||
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
|
||||
|
||||
if (!kernel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell) {
|
||||
return withToken(token => (kernel.executeCell as any)(document.notebookDocument, cell.cell, token));
|
||||
} else {
|
||||
return withToken(token => (kernel.executeAllCells as any)(document.notebookDocument, token));
|
||||
}
|
||||
const extCellRange = cellRange.map(c => typeConverters.NotebookCellRange.to(c));
|
||||
return kernel.executeCellsRequest(document.notebookDocument, extCellRange);
|
||||
}
|
||||
|
||||
async cancelNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
|
||||
async interruptNotebookExecution(kernelId: string, document: ExtHostNotebookDocument): Promise<void> {
|
||||
const kernel = this._friendlyIdToKernel.get(document.uri)?.get(kernelId);
|
||||
|
||||
if (!kernel) {
|
||||
if (!kernel || !kernel.interrupt) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell) {
|
||||
return kernel.cancelCellExecution(document.notebookDocument, cell.cell);
|
||||
} else {
|
||||
return kernel.cancelAllCellsExecution(document.notebookDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO@roblou remove 'token' passed to all execute APIs once extensions are updated
|
||||
async function withToken(cb: (token: CancellationToken) => any) {
|
||||
const source = new CancellationTokenSource();
|
||||
try {
|
||||
await cb(source.token);
|
||||
} finally {
|
||||
source.dispose();
|
||||
return kernel.interrupt(document.notebookDocument);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +193,7 @@ export class NotebookEditorDecorationType {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type NotebookContentProviderData = {
|
||||
readonly provider: vscode.NotebookContentProvider;
|
||||
readonly extension: IExtensionDescription;
|
||||
@@ -232,12 +219,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event;
|
||||
private readonly _onDidChangeCellOutputs = new Emitter<vscode.NotebookCellOutputsChangeEvent>();
|
||||
readonly onDidChangeCellOutputs = this._onDidChangeCellOutputs.event;
|
||||
private readonly _onDidChangeCellLanguage = new Emitter<vscode.NotebookCellLanguageChangeEvent>();
|
||||
readonly onDidChangeCellLanguage = this._onDidChangeCellLanguage.event;
|
||||
private readonly _onDidChangeCellMetadata = new Emitter<vscode.NotebookCellMetadataChangeEvent>();
|
||||
readonly onDidChangeCellMetadata = this._onDidChangeCellMetadata.event;
|
||||
private readonly _onDidChangeActiveNotebookEditor = new Emitter<vscode.NotebookEditor | undefined>();
|
||||
readonly onDidChangeActiveNotebookEditor = this._onDidChangeActiveNotebookEditor.event;
|
||||
private readonly _onDidChangeCellExecutionState = new Emitter<vscode.NotebookCellExecutionStateChangeEvent>();
|
||||
readonly onDidChangeNotebookCellExecutionState = this._onDidChangeCellExecutionState.event;
|
||||
|
||||
private _activeNotebookEditor: ExtHostNotebookEditor | undefined;
|
||||
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
|
||||
@@ -259,10 +246,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
|
||||
onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event;
|
||||
|
||||
private _activeExecutions = new ResourceMap<NotebookCellExecutionTask>();
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
commands: ExtHostCommands,
|
||||
private _documentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private _textDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private _textDocuments: ExtHostDocuments,
|
||||
private readonly _webviewInitData: WebviewInitData,
|
||||
private readonly logService: ILogService,
|
||||
private readonly _extensionStoragePaths: IExtensionStoragePaths,
|
||||
@@ -337,7 +327,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
let listener: IDisposable | undefined;
|
||||
if (provider.onDidChangeNotebookContentOptions) {
|
||||
listener = provider.onDidChangeNotebookContentOptions(() => {
|
||||
this._proxy.$updateNotebookProviderOptions(viewType, provider.options);
|
||||
const internalOptions = typeConverters.NotebookDocumentContentOptions.from(provider.options);
|
||||
this._proxy.$updateNotebookProviderOptions(viewType, internalOptions);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -349,9 +340,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
console.warn(`Notebook content provider view options file name pattern is invalid ${options?.viewOptions?.filenamePattern}`);
|
||||
}
|
||||
|
||||
const internalOptions = typeConverters.NotebookDocumentContentOptions.from(options);
|
||||
this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, {
|
||||
transientOutputs: options?.transientOutputs || false,
|
||||
transientMetadata: options?.transientMetadata || {},
|
||||
transientOutputs: internalOptions.transientOutputs,
|
||||
transientMetadata: internalOptions.transientMetadata,
|
||||
viewOptions: options?.viewOptions && viewOptionsFilenamePattern ? { displayName: options.viewOptions.displayName, filenamePattern: viewOptionsFilenamePattern, exclusive: options.viewOptions.exclusive || false } : undefined
|
||||
});
|
||||
|
||||
@@ -382,13 +374,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
return new NotebookEditorDecorationType(this._proxy, options).value;
|
||||
}
|
||||
|
||||
async openNotebookDocument(uriComponents: UriComponents, viewType?: string): Promise<vscode.NotebookDocument> {
|
||||
const cached = this._documents.get(URI.revive(uriComponents));
|
||||
async openNotebookDocument(uri: URI): Promise<vscode.NotebookDocument> {
|
||||
const cached = this._documents.get(uri);
|
||||
if (cached) {
|
||||
return cached.notebookDocument;
|
||||
}
|
||||
await this._proxy.$tryOpenDocument(uriComponents, viewType);
|
||||
const document = this._documents.get(URI.revive(uriComponents));
|
||||
const canonicalUri = await this._proxy.$tryOpenDocument(uri);
|
||||
const document = this._documents.get(URI.revive(canonicalUri));
|
||||
return assertIsDefined(document?.notebookDocument);
|
||||
}
|
||||
|
||||
@@ -408,7 +400,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
return callback(provider, document);
|
||||
}
|
||||
|
||||
async showNotebookDocument(notebookDocument: vscode.NotebookDocument, options?: vscode.NotebookDocumentShowOptions): Promise<vscode.NotebookEditor> {
|
||||
async showNotebookDocument(notebookOrUri: vscode.NotebookDocument | URI, options?: vscode.NotebookDocumentShowOptions): Promise<vscode.NotebookEditor> {
|
||||
|
||||
if (URI.isUri(notebookOrUri)) {
|
||||
notebookOrUri = await this.openNotebookDocument(notebookOrUri);
|
||||
}
|
||||
|
||||
let resolvedOptions: INotebookDocumentShowOptions;
|
||||
if (typeof options === 'object') {
|
||||
resolvedOptions = {
|
||||
@@ -423,7 +420,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
};
|
||||
}
|
||||
|
||||
const editorId = await this._proxy.$tryShowNotebookDocument(notebookDocument.uri, notebookDocument.viewType, resolvedOptions);
|
||||
const editorId = await this._proxy.$tryShowNotebookDocument(notebookOrUri.uri, notebookOrUri.viewType, resolvedOptions);
|
||||
const editor = editorId && this._editors.get(editorId)?.editor;
|
||||
|
||||
if (editor) {
|
||||
@@ -431,9 +428,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
}
|
||||
|
||||
if (editorId) {
|
||||
throw new Error(`Could NOT open editor for "${notebookDocument.toString()}" because another editor opened in the meantime.`);
|
||||
throw new Error(`Could NOT open editor for "${notebookOrUri.toString()}" because another editor opened in the meantime.`);
|
||||
} else {
|
||||
throw new Error(`Could NOT open editor for "${notebookDocument.toString()}".`);
|
||||
throw new Error(`Could NOT open editor for "${notebookOrUri.toString()}".`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,32 +471,88 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
await provider.provider.resolveNotebook(document.notebookDocument, webComm.contentProviderComm);
|
||||
}
|
||||
|
||||
async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void> {
|
||||
async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void> {
|
||||
await this._withAdapter(handle, uri, async (adapter, document) => {
|
||||
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
|
||||
return adapter.executeNotebook(kernelId, document, cell);
|
||||
return adapter.executeNotebook(kernelId, document, cellRange);
|
||||
});
|
||||
}
|
||||
|
||||
async $cancelNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined): Promise<void> {
|
||||
await this._withAdapter(handle, uri, async (adapter, document) => {
|
||||
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
// --- serialize/deserialize
|
||||
|
||||
return adapter.cancelNotebook(kernelId, document, cell);
|
||||
private _handlePool = 0;
|
||||
private readonly _notebookSerializer = new Map<number, vscode.NotebookSerializer>();
|
||||
|
||||
registerNotebookSerializer(extension: IExtensionDescription, viewType: string, serializer: vscode.NotebookSerializer, options?: vscode.NotebookDocumentContentOptions): vscode.Disposable {
|
||||
const handle = this._handlePool++;
|
||||
this._notebookSerializer.set(handle, serializer);
|
||||
const internalOptions = typeConverters.NotebookDocumentContentOptions.from(options);
|
||||
this._proxy.$registerNotebookSerializer(
|
||||
handle,
|
||||
{ id: extension.identifier, location: extension.extensionLocation, description: extension.description },
|
||||
viewType,
|
||||
internalOptions
|
||||
);
|
||||
return toDisposable(() => {
|
||||
this._proxy.$unregisterNotebookSerializer(handle);
|
||||
});
|
||||
}
|
||||
|
||||
async $dataToNotebook(handle: number, bytes: VSBuffer): Promise<NotebookDataDto> {
|
||||
const serializer = this._notebookSerializer.get(handle);
|
||||
if (!serializer) {
|
||||
throw new Error('NO serializer found');
|
||||
}
|
||||
const data = await serializer.dataToNotebook(bytes.buffer);
|
||||
return {
|
||||
metadata: typeConverters.NotebookDocumentMetadata.from(data.metadata),
|
||||
cells: data.cells.map(typeConverters.NotebookCellData.from),
|
||||
};
|
||||
}
|
||||
|
||||
async $notebookToData(handle: number, data: NotebookDataDto): Promise<VSBuffer> {
|
||||
const serializer = this._notebookSerializer.get(handle);
|
||||
if (!serializer) {
|
||||
throw new Error('NO serializer found');
|
||||
}
|
||||
const bytes = await serializer.notebookToData({
|
||||
metadata: typeConverters.NotebookDocumentMetadata.to(data.metadata),
|
||||
cells: data.cells.map(typeConverters.NotebookCellData.to)
|
||||
});
|
||||
return VSBuffer.wrap(bytes);
|
||||
}
|
||||
|
||||
async $cancelNotebookCellExecution(handle: number, uri: UriComponents, kernelId: string, cellRange: ICellRange[]): Promise<void> {
|
||||
await this._withAdapter(handle, uri, async (adapter, document) => {
|
||||
return adapter.interruptNotebookExecution(kernelId, document);
|
||||
});
|
||||
|
||||
const document = this._documents.get(URI.revive(uri));
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let range of cellRange) {
|
||||
for (let i = range.start; i < range.end; i++) {
|
||||
const cell = document.getCellFromIndex(i);
|
||||
if (cell) {
|
||||
this.cancelOneNotebookCellExecution(cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private cancelOneNotebookCellExecution(cell: ExtHostCell): void {
|
||||
const execution = this._activeExecutions.get(cell.uri);
|
||||
execution?.cancel();
|
||||
}
|
||||
|
||||
// --- open, save, saveAs, backup
|
||||
|
||||
async $openNotebook(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto> {
|
||||
async $openNotebook(viewType: string, uri: UriComponents, backupId: string | undefined, untitledDocumentData: VSBuffer | undefined, token: CancellationToken): Promise<NotebookDataDto> {
|
||||
const { provider } = this._getProviderData(viewType);
|
||||
const data = await provider.openNotebook(URI.revive(uri), { backupId });
|
||||
const data = await provider.openNotebook(URI.revive(uri), { backupId, untitledDocumentData: untitledDocumentData?.buffer }, token);
|
||||
return {
|
||||
metadata: {
|
||||
...notebookDocumentMetadataDefaults,
|
||||
...data.metadata
|
||||
},
|
||||
metadata: typeConverters.NotebookDocumentMetadata.from(data.metadata),
|
||||
cells: data.cells.map(typeConverters.NotebookCellData.from),
|
||||
};
|
||||
}
|
||||
@@ -569,32 +622,31 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
$acceptEditorPropertiesChanged(id: string, data: INotebookEditorPropertiesChangeData): void {
|
||||
this.logService.debug('ExtHostNotebook#$acceptEditorPropertiesChanged', id, data);
|
||||
|
||||
let editor: { editor: ExtHostNotebookEditor; } | undefined;
|
||||
this._editors.forEach(e => {
|
||||
if (e.editor.id === id) {
|
||||
editor = e;
|
||||
}
|
||||
});
|
||||
|
||||
const editor = this._editors.get(id);
|
||||
if (!editor) {
|
||||
return;
|
||||
throw new Error(`unknown text editor: ${id}`);
|
||||
}
|
||||
|
||||
// ONE: make all state updates
|
||||
if (data.visibleRanges) {
|
||||
editor.editor._acceptVisibleRanges(data.visibleRanges.ranges.map(typeConverters.NotebookCellRange.to));
|
||||
}
|
||||
if (data.selections) {
|
||||
editor.editor._acceptSelections(data.selections.selections.map(typeConverters.NotebookCellRange.to));
|
||||
}
|
||||
|
||||
// TWO: send all events after states have been updated
|
||||
if (data.visibleRanges) {
|
||||
this._onDidChangeNotebookEditorVisibleRanges.fire({
|
||||
notebookEditor: editor.editor.editor,
|
||||
visibleRanges: editor.editor.editor.visibleRanges
|
||||
});
|
||||
}
|
||||
|
||||
if (data.selections) {
|
||||
editor.editor._acceptSelections(data.selections.selections);
|
||||
|
||||
this._onDidChangeNotebookEditorSelection.fire({
|
||||
this._onDidChangeNotebookEditorSelection.fire(Object.freeze({
|
||||
notebookEditor: editor.editor.editor,
|
||||
selection: editor.editor.editor.selection
|
||||
});
|
||||
selections: editor.editor.editor.selections
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,9 +654,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
this.logService.debug('ExtHostNotebook#$acceptDocumentPropertiesChanged', uri.path, data);
|
||||
const document = this._getNotebookDocument(URI.revive(uri));
|
||||
document.acceptDocumentPropertiesChanged(data);
|
||||
if (data.metadata) {
|
||||
this._onDidChangeNotebookDocumentMetadata.fire({ document: document.notebookDocument });
|
||||
}
|
||||
}
|
||||
|
||||
private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: ICellRange[], visibleRanges: extHostTypes.NotebookCellRange[]) {
|
||||
private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, data: INotebookEditorAddData) {
|
||||
const revivedUri = document.uri;
|
||||
let webComm = this._webviewComm.get(editorId);
|
||||
|
||||
@@ -617,11 +672,12 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
editorId,
|
||||
document.notebookDocument.viewType,
|
||||
this._proxy,
|
||||
document
|
||||
document,
|
||||
data.visibleRanges.map(typeConverters.NotebookCellRange.to),
|
||||
data.selections.map(typeConverters.NotebookCellRange.to),
|
||||
typeof data.viewColumn === 'number' ? typeConverters.ViewColumn.to(data.viewColumn) : undefined
|
||||
);
|
||||
|
||||
editor._acceptSelections(selections);
|
||||
editor._acceptVisibleRanges(visibleRanges);
|
||||
|
||||
this._editors.get(editorId)?.editor.dispose();
|
||||
this._editors.set(editorId, { editor });
|
||||
@@ -638,7 +694,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
if (document) {
|
||||
document.dispose();
|
||||
this._documents.delete(revivedUri);
|
||||
this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.notebookDocument.cells.map(cell => cell.uri) });
|
||||
this._textDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ removedDocuments: document.notebookDocument.cells.map(cell => cell.document.uri) });
|
||||
this._onDidCloseNotebookDocument.fire(document.notebookDocument);
|
||||
}
|
||||
|
||||
@@ -667,7 +723,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
|
||||
const document = new ExtHostNotebookDocument(
|
||||
this._proxy,
|
||||
this._documentsAndEditors,
|
||||
this._textDocumentsAndEditors,
|
||||
this._textDocuments,
|
||||
{
|
||||
emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
|
||||
that._onDidChangeNotebookCells.fire(event);
|
||||
@@ -675,34 +732,24 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void {
|
||||
that._onDidChangeCellOutputs.fire(event);
|
||||
},
|
||||
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
|
||||
that._onDidChangeCellLanguage.fire(event);
|
||||
},
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
|
||||
that._onDidChangeCellMetadata.fire(event);
|
||||
},
|
||||
emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void {
|
||||
that._onDidChangeNotebookDocumentMetadata.fire(event);
|
||||
emitCellExecutionStateChange(event: vscode.NotebookCellExecutionStateChangeEvent): void {
|
||||
that._onDidChangeCellExecutionState.fire(event);
|
||||
}
|
||||
},
|
||||
viewType,
|
||||
modelData.contentOptions,
|
||||
modelData.metadata ? typeConverters.NotebookDocumentMetadata.to(modelData.metadata) : new extHostTypes.NotebookDocumentMetadata(),
|
||||
uri,
|
||||
);
|
||||
|
||||
document.acceptModelChanged({
|
||||
versionId: modelData.versionId,
|
||||
rawEvents: [
|
||||
{
|
||||
kind: NotebookCellsChangeType.Initialize,
|
||||
changes: [[
|
||||
0,
|
||||
0,
|
||||
modelData.cells
|
||||
]]
|
||||
}
|
||||
]
|
||||
rawEvents: [{
|
||||
kind: NotebookCellsChangeType.Initialize,
|
||||
changes: [[0, 0, modelData.cells]]
|
||||
}]
|
||||
}, false);
|
||||
|
||||
// add cell document as vscode.TextDocument
|
||||
@@ -710,7 +757,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
|
||||
this._documents.get(uri)?.dispose();
|
||||
this._documents.set(uri, document);
|
||||
this._documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments });
|
||||
this._textDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: addedCellDocuments });
|
||||
|
||||
this._onDidOpenNotebookDocument.fire(document.notebookDocument);
|
||||
}
|
||||
@@ -726,7 +773,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
const document = this._documents.get(revivedUri);
|
||||
|
||||
if (document) {
|
||||
this._createExtHostEditor(document, editorModelData.id, editorModelData.selections, editorModelData.visibleRanges.map(typeConverters.NotebookCellRange.to));
|
||||
this._createExtHostEditor(document, editorModelData.id, editorModelData);
|
||||
editorChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -771,23 +818,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
this._onDidChangeVisibleNotebookEditors.fire(this.visibleNotebookEditors);
|
||||
}
|
||||
|
||||
if (delta.newActiveEditor === null) {
|
||||
// clear active notebook as current active editor is non-notebook editor
|
||||
this._activeNotebookEditor = undefined;
|
||||
} else if (delta.newActiveEditor) {
|
||||
this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
|
||||
}
|
||||
if (delta.newActiveEditor !== undefined) {
|
||||
if (delta.newActiveEditor) {
|
||||
this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
|
||||
this._activeNotebookEditor?._acceptActive(true);
|
||||
for (const e of this._editors.values()) {
|
||||
if (e.editor !== this._activeNotebookEditor) {
|
||||
e.editor._acceptActive(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// clear active notebook as current active editor is non-notebook editor
|
||||
this._activeNotebookEditor = undefined;
|
||||
for (const e of this._editors.values()) {
|
||||
e.editor._acceptActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor?.editor);
|
||||
}
|
||||
}
|
||||
@@ -796,7 +833,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
const statusBarItem = new NotebookCellStatusBarItemInternal(this._proxy, this._commandsConverter, cell, alignment, priority);
|
||||
|
||||
// Look up the ExtHostCell for this NotebookCell URI, bind to its disposable lifecycle
|
||||
const parsedUri = CellUri.parse(cell.uri);
|
||||
const parsedUri = CellUri.parse(cell.document.uri);
|
||||
if (parsedUri) {
|
||||
const document = this._documents.get(parsedUri.notebook);
|
||||
if (document) {
|
||||
@@ -809,6 +846,35 @@ export class ExtHostNotebookController implements ExtHostNotebookShape {
|
||||
|
||||
return statusBarItem;
|
||||
}
|
||||
|
||||
createNotebookCellExecution(docUri: vscode.Uri, index: number, kernelId: string): vscode.NotebookCellExecutionTask | undefined {
|
||||
const document = this.lookupNotebookDocument(docUri);
|
||||
if (!document) {
|
||||
throw new Error(`Invalid cell uri/index: ${docUri}, ${index}`);
|
||||
}
|
||||
|
||||
const cell = document.getCellFromIndex(index);
|
||||
if (!cell) {
|
||||
throw new Error(`Invalid cell uri/index: ${docUri}, ${index}`);
|
||||
}
|
||||
|
||||
// TODO@roblou also validate kernelId, once kernel has moved from editor to document
|
||||
if (this._activeExecutions.has(cell.uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const execution = new NotebookCellExecutionTask(docUri, document, cell, this._proxy);
|
||||
this._activeExecutions.set(cell.uri, execution);
|
||||
const listener = execution.onDidChangeState(() => {
|
||||
if (execution.state === NotebookCellExecutionTaskState.Resolved) {
|
||||
execution.dispose();
|
||||
listener.dispose();
|
||||
this._activeExecutions.delete(cell.uri);
|
||||
}
|
||||
});
|
||||
|
||||
return execution.asApiObject();
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellStatusBarItemInternal extends Disposable {
|
||||
@@ -946,7 +1012,7 @@ export class NotebookCellStatusBarItemInternal extends Disposable {
|
||||
|
||||
const entry: INotebookCellStatusBarEntry = {
|
||||
alignment: this.alignment === extHostTypes.NotebookCellStatusBarAlignment.Left ? CellStatusbarAlignment.LEFT : CellStatusbarAlignment.RIGHT,
|
||||
cellResource: this.cell.uri,
|
||||
cellResource: this.cell.document.uri,
|
||||
command: this._command?.internal,
|
||||
text: this.text,
|
||||
tooltip: this.tooltip,
|
||||
@@ -985,3 +1051,157 @@ function createNotebookCellStatusBarApiItem(internalItem: NotebookCellStatusBarI
|
||||
dispose() { internalItem.dispose(); }
|
||||
});
|
||||
}
|
||||
|
||||
enum NotebookCellExecutionTaskState {
|
||||
Init,
|
||||
Started,
|
||||
Resolved
|
||||
}
|
||||
|
||||
class NotebookCellExecutionTask extends Disposable {
|
||||
private _onDidChangeState = new Emitter<void>();
|
||||
readonly onDidChangeState = this._onDidChangeState.event;
|
||||
|
||||
private _state = NotebookCellExecutionTaskState.Init;
|
||||
get state(): NotebookCellExecutionTaskState { return this._state; }
|
||||
|
||||
private readonly _tokenSource: CancellationTokenSource;
|
||||
|
||||
private _executionOrder: number | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _uri: vscode.Uri,
|
||||
private readonly _document: ExtHostNotebookDocument,
|
||||
private readonly _cell: ExtHostCell,
|
||||
private readonly _proxy: MainThreadNotebookShape) {
|
||||
super();
|
||||
this._tokenSource = this._register(new CancellationTokenSource());
|
||||
|
||||
this._executionOrder = _cell.internalMetadata.executionOrder;
|
||||
this.mixinMetadata({
|
||||
runState: NotebookCellExecutionState.Pending,
|
||||
lastRunDuration: null,
|
||||
executionOrder: null
|
||||
});
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this._tokenSource.cancel();
|
||||
}
|
||||
|
||||
private async applyEdits(edits: IImmediateCellEditOperation[]): Promise<void> {
|
||||
return this._proxy.$applyEdits(this._uri, edits, false);
|
||||
}
|
||||
|
||||
private verifyStateForOutput() {
|
||||
if (this._state === NotebookCellExecutionTaskState.Init) {
|
||||
throw new Error('Must call start before modifying cell output');
|
||||
}
|
||||
|
||||
if (this._state === NotebookCellExecutionTaskState.Resolved) {
|
||||
throw new Error('Cannot modify cell output after calling resolve');
|
||||
}
|
||||
}
|
||||
|
||||
private mixinMetadata(mixinMetadata: NullablePartialNotebookCellMetadata) {
|
||||
const edits: IImmediateCellEditOperation[] = [
|
||||
{ editType: CellEditType.PartialMetadata, handle: this._cell.handle, metadata: mixinMetadata }
|
||||
];
|
||||
this.applyEdits(edits);
|
||||
}
|
||||
|
||||
private cellIndexToHandle(cellIndex: number | undefined): number | undefined {
|
||||
const cell = typeof cellIndex === 'number' ? this._document.getCellFromIndex(cellIndex) : this._cell;
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
|
||||
return cell.handle;
|
||||
}
|
||||
|
||||
asApiObject(): vscode.NotebookCellExecutionTask {
|
||||
const that = this;
|
||||
return Object.freeze(<vscode.NotebookCellExecutionTask>{
|
||||
get document() { return that._document.notebookDocument; },
|
||||
get cell() { return that._cell.cell; },
|
||||
|
||||
get executionOrder() { return that._executionOrder; },
|
||||
set executionOrder(v: number | undefined) {
|
||||
that._executionOrder = v;
|
||||
that.mixinMetadata({
|
||||
executionOrder: v
|
||||
});
|
||||
},
|
||||
|
||||
start(context?: vscode.NotebookCellExecuteStartContext): void {
|
||||
if (that._state === NotebookCellExecutionTaskState.Resolved || that._state === NotebookCellExecutionTaskState.Started) {
|
||||
throw new Error('Cannot call start again');
|
||||
}
|
||||
|
||||
that._state = NotebookCellExecutionTaskState.Started;
|
||||
that._onDidChangeState.fire();
|
||||
|
||||
that.mixinMetadata({
|
||||
runState: NotebookCellExecutionState.Executing,
|
||||
runStartTime: context?.startTime
|
||||
});
|
||||
},
|
||||
|
||||
end(result?: vscode.NotebookCellExecuteEndContext): void {
|
||||
if (that._state === NotebookCellExecutionTaskState.Resolved) {
|
||||
throw new Error('Cannot call resolve twice');
|
||||
}
|
||||
|
||||
that._state = NotebookCellExecutionTaskState.Resolved;
|
||||
that._onDidChangeState.fire();
|
||||
|
||||
that.mixinMetadata({
|
||||
runState: NotebookCellExecutionState.Idle,
|
||||
lastRunSuccess: result?.success ?? null,
|
||||
lastRunDuration: result?.duration ?? null,
|
||||
});
|
||||
},
|
||||
|
||||
clearOutput(cellIndex?: number): Thenable<void> {
|
||||
that.verifyStateForOutput();
|
||||
return this.replaceOutput([], cellIndex);
|
||||
},
|
||||
|
||||
async appendOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
const handle = that.cellIndexToHandle(cellIndex);
|
||||
if (typeof handle !== 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
outputs = Array.isArray(outputs) ? outputs : [outputs];
|
||||
return that.applyEdits([{ editType: CellEditType.Output, handle, append: true, outputs: outputs.map(typeConverters.NotebookCellOutput.from) }]);
|
||||
},
|
||||
|
||||
async replaceOutput(outputs: vscode.NotebookCellOutput | vscode.NotebookCellOutput[], cellIndex?: number): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
const handle = that.cellIndexToHandle(cellIndex);
|
||||
if (typeof handle !== 'number') {
|
||||
return;
|
||||
}
|
||||
|
||||
outputs = Array.isArray(outputs) ? outputs : [outputs];
|
||||
return that.applyEdits([{ editType: CellEditType.Output, handle, outputs: outputs.map(typeConverters.NotebookCellOutput.from) }]);
|
||||
},
|
||||
|
||||
async appendOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
items = Array.isArray(items) ? items : [items];
|
||||
return that.applyEdits([{ editType: CellEditType.OutputItems, append: true, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId }]);
|
||||
},
|
||||
|
||||
async replaceOutputItems(items: vscode.NotebookCellOutputItem | vscode.NotebookCellOutputItem[], outputId: string): Promise<void> {
|
||||
that.verifyStateForOutput();
|
||||
items = Array.isArray(items) ? items : [items];
|
||||
return that.applyEdits([{ editType: CellEditType.OutputItems, items: items.map(typeConverters.NotebookCellOutputItem.from), outputId }]);
|
||||
},
|
||||
|
||||
token: that._tokenSource.token
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
}
|
||||
};
|
||||
|
||||
this._disposables.add(extHostNotebooks.onDidChangeCellLanguage(e => documentChange(e.document)));
|
||||
this._disposables.add(extHostNotebooks.onDidChangeNotebookCells(e => documentChange(e.document)));
|
||||
}
|
||||
|
||||
@@ -75,8 +74,8 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
const cellLengths: number[] = [];
|
||||
const cellLineCounts: number[] = [];
|
||||
for (const cell of this._notebook.cells) {
|
||||
if (cell.cellKind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) {
|
||||
this._cellUris.set(cell.uri, this._cells.length);
|
||||
if (cell.kind === types.NotebookCellKind.Code && (!this._selector || score(this._selector, cell.document.uri, cell.document.languageId, true))) {
|
||||
this._cellUris.set(cell.document.uri, this._cells.length);
|
||||
this._cells.push(cell);
|
||||
cellLengths.push(cell.document.getText().length + 1);
|
||||
cellLineCounts.push(cell.document.lineCount);
|
||||
@@ -162,7 +161,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
const range = new types.Range(startPos, endPos);
|
||||
|
||||
const startCell = this._cells[startIdx.index];
|
||||
return new types.Location(startCell.uri, <types.Range>startCell.document.validateRange(range));
|
||||
return new types.Location(startCell.document.uri, <types.Range>startCell.document.validateRange(range));
|
||||
}
|
||||
|
||||
contains(uri: vscode.Uri): boolean {
|
||||
|
||||
@@ -6,24 +6,28 @@
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { deepFreeze, equals } from 'vs/base/common/objects';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { CellKind, INotebookDocumentPropertiesChangeData, MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostModelAddedData } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import * as extHostTypeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IMainCellDto, IOutputDto, IOutputItemDto, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellsSplice2 } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
class RawContentChangeEvent {
|
||||
|
||||
constructor(readonly start: number, readonly deletedCount: number, readonly deletedItems: vscode.NotebookCell[], readonly items: ExtHostCell[]) { }
|
||||
|
||||
static asApiEvent(event: RawContentChangeEvent): vscode.NotebookCellsChangeData {
|
||||
return Object.freeze({
|
||||
start: event.start,
|
||||
deletedCount: event.deletedCount,
|
||||
deletedItems: event.deletedItems,
|
||||
items: event.items.map(data => data.cell)
|
||||
static asApiEvents(events: RawContentChangeEvent[]): readonly vscode.NotebookCellsChangeData[] {
|
||||
return events.map(event => {
|
||||
return {
|
||||
start: event.start,
|
||||
deletedCount: event.deletedCount,
|
||||
deletedItems: event.deletedItems,
|
||||
items: event.items.map(data => data.cell)
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -47,7 +51,9 @@ export class ExtHostCell {
|
||||
|
||||
private _outputs: extHostTypes.NotebookCellOutput[];
|
||||
private _metadata: extHostTypes.NotebookCellMetadata;
|
||||
private _previousResult: vscode.NotebookCellExecutionSummary | undefined;
|
||||
|
||||
private _internalMetadata: NotebookCellMetadata;
|
||||
readonly handle: number;
|
||||
readonly uri: URI;
|
||||
readonly cellKind: CellKind;
|
||||
@@ -63,7 +69,9 @@ export class ExtHostCell {
|
||||
this.uri = URI.revive(_cellData.uri);
|
||||
this.cellKind = _cellData.cellKind;
|
||||
this._outputs = _cellData.outputs.map(extHostTypeConverters.NotebookCellOutput.to);
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(_cellData.metadata ?? {});
|
||||
this._internalMetadata = _cellData.metadata ?? {};
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(this._internalMetadata);
|
||||
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(this._internalMetadata);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -71,6 +79,10 @@ export class ExtHostCell {
|
||||
this._onDidDispose.dispose();
|
||||
}
|
||||
|
||||
get internalMetadata(): NotebookCellMetadata {
|
||||
return this._internalMetadata;
|
||||
}
|
||||
|
||||
get cell(): vscode.NotebookCell {
|
||||
if (!this._cell) {
|
||||
const that = this;
|
||||
@@ -78,17 +90,14 @@ export class ExtHostCell {
|
||||
if (!data) {
|
||||
throw new Error(`MISSING extHostDocument for notebook cell: ${this.uri}`);
|
||||
}
|
||||
this._cell = Object.freeze({
|
||||
this._cell = Object.freeze<vscode.NotebookCell>({
|
||||
get index() { return that._notebook.getCellIndex(that); },
|
||||
notebook: that._notebook.notebookDocument,
|
||||
uri: that.uri,
|
||||
cellKind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind),
|
||||
kind: extHostTypeConverters.NotebookCellKind.to(this._cellData.cellKind),
|
||||
document: data.document,
|
||||
get language() { return data!.document.languageId; },
|
||||
get outputs() { return that._outputs.slice(0); },
|
||||
set outputs(_value) { throw new Error('Use WorkspaceEdit to update cell outputs.'); },
|
||||
get metadata() { return that._metadata; },
|
||||
set metadata(_value) { throw new Error('Use WorkspaceEdit to update cell metadata.'); },
|
||||
get latestExecutionSummary() { return that._previousResult; }
|
||||
});
|
||||
}
|
||||
return this._cell;
|
||||
@@ -110,16 +119,17 @@ export class ExtHostCell {
|
||||
}
|
||||
|
||||
setMetadata(newMetadata: NotebookCellMetadata): void {
|
||||
this._internalMetadata = newMetadata;
|
||||
this._metadata = extHostTypeConverters.NotebookCellMetadata.to(newMetadata);
|
||||
this._previousResult = extHostTypeConverters.NotebookCellPreviousExecutionResult.to(newMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
export interface INotebookEventEmitter {
|
||||
emitModelChange(events: vscode.NotebookCellsChangeEvent): void;
|
||||
emitDocumentMetadataChange(event: vscode.NotebookDocumentMetadataChangeEvent): void;
|
||||
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void;
|
||||
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void;
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void;
|
||||
emitCellExecutionStateChange(event: vscode.NotebookCellExecutionStateChangeEvent): void;
|
||||
}
|
||||
|
||||
|
||||
@@ -140,10 +150,10 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private readonly _documentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private readonly _textDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private readonly _textDocuments: ExtHostDocuments,
|
||||
private readonly _emitter: INotebookEventEmitter,
|
||||
private readonly _viewType: string,
|
||||
private readonly _contentOptions: vscode.NotebookDocumentContentOptions,
|
||||
private _metadata: extHostTypes.NotebookDocumentMetadata,
|
||||
readonly uri: URI,
|
||||
) {
|
||||
@@ -170,7 +180,6 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
get cells(): ReadonlyArray<vscode.NotebookCell> { return that._cells.map(cell => cell.cell); },
|
||||
get metadata() { return that._metadata; },
|
||||
set metadata(_value: Required<vscode.NotebookDocumentMetadata>) { throw new Error('Use WorkspaceEdit to update metadata.'); },
|
||||
get contentOptions() { return that._contentOptions; },
|
||||
save() { return that._save(); }
|
||||
});
|
||||
}
|
||||
@@ -188,39 +197,37 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
}
|
||||
|
||||
acceptDocumentPropertiesChanged(data: INotebookDocumentPropertiesChangeData) {
|
||||
const newMetadata = {
|
||||
...notebookDocumentMetadataDefaults,
|
||||
...data.metadata
|
||||
};
|
||||
this._metadata = this._metadata.with(newMetadata);
|
||||
this._emitter.emitDocumentMetadataChange({ document: this.notebookDocument });
|
||||
if (data.metadata) {
|
||||
this._metadata = this._metadata.with(data.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
acceptModelChanged(event: NotebookCellsChangedEventDto, isDirty: boolean): void {
|
||||
this._versionId = event.versionId;
|
||||
this._isDirty = isDirty;
|
||||
event.rawEvents.forEach(e => {
|
||||
if (e.kind === NotebookCellsChangeType.Initialize) {
|
||||
this._spliceNotebookCells(e.changes, true);
|
||||
} if (e.kind === NotebookCellsChangeType.ModelChange) {
|
||||
this._spliceNotebookCells(e.changes, false);
|
||||
} else if (e.kind === NotebookCellsChangeType.Move) {
|
||||
this._moveCell(e.index, e.newIdx);
|
||||
} else if (e.kind === NotebookCellsChangeType.Output) {
|
||||
this._setCellOutputs(e.index, e.outputs);
|
||||
} else if (e.kind === NotebookCellsChangeType.OutputItem) {
|
||||
this._setCellOutputItems(e.index, e.outputId, e.append, e.outputItems);
|
||||
} else if (e.kind === NotebookCellsChangeType.ChangeLanguage) {
|
||||
this._changeCellLanguage(e.index, e.language);
|
||||
} else if (e.kind === NotebookCellsChangeType.ChangeCellMetadata) {
|
||||
this._changeCellMetadata(e.index, e.metadata);
|
||||
|
||||
for (const rawEvent of event.rawEvents) {
|
||||
if (rawEvent.kind === NotebookCellsChangeType.Initialize) {
|
||||
this._spliceNotebookCells(rawEvent.changes, true);
|
||||
} if (rawEvent.kind === NotebookCellsChangeType.ModelChange) {
|
||||
this._spliceNotebookCells(rawEvent.changes, false);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.Move) {
|
||||
this._moveCell(rawEvent.index, rawEvent.newIdx);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.Output) {
|
||||
this._setCellOutputs(rawEvent.index, rawEvent.outputs);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.OutputItem) {
|
||||
this._setCellOutputItems(rawEvent.index, rawEvent.outputId, rawEvent.append, rawEvent.outputItems);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.ChangeLanguage) {
|
||||
this._changeCellLanguage(rawEvent.index, rawEvent.language);
|
||||
} else if (rawEvent.kind === NotebookCellsChangeType.ChangeCellMetadata) {
|
||||
this._changeCellMetadata(rawEvent.index, rawEvent.metadata);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _save(): Promise<boolean> {
|
||||
if (this._disposed) {
|
||||
return Promise.reject(new Error('Document has been closed'));
|
||||
return Promise.reject(new Error('Notebook has been closed'));
|
||||
}
|
||||
return this._proxy.$trySaveDocument(this.uri);
|
||||
}
|
||||
@@ -238,7 +245,7 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
const cellDtos = splice[2];
|
||||
const newCells = cellDtos.map(cell => {
|
||||
|
||||
const extCell = new ExtHostCell(this, this._documentsAndEditors, cell);
|
||||
const extCell = new ExtHostCell(this, this._textDocumentsAndEditors, cell);
|
||||
|
||||
if (!initialization) {
|
||||
addedCellDocuments.push(ExtHostCell.asModelAddData(this.notebookDocument, cell));
|
||||
@@ -268,62 +275,67 @@ export class ExtHostNotebookDocument extends Disposable {
|
||||
contentChangeEvents.push(changeEvent);
|
||||
});
|
||||
|
||||
this._documentsAndEditors.acceptDocumentsAndEditorsDelta({
|
||||
this._textDocumentsAndEditors.acceptDocumentsAndEditorsDelta({
|
||||
addedDocuments: addedCellDocuments,
|
||||
removedDocuments: removedCellDocuments
|
||||
});
|
||||
|
||||
if (!initialization) {
|
||||
this._emitter.emitModelChange({
|
||||
this._emitter.emitModelChange(deepFreeze({
|
||||
document: this.notebookDocument,
|
||||
changes: contentChangeEvents.map(RawContentChangeEvent.asApiEvent)
|
||||
});
|
||||
changes: RawContentChangeEvent.asApiEvents(contentChangeEvents)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private _moveCell(index: number, newIdx: number): void {
|
||||
const cells = this._cells.splice(index, 1);
|
||||
this._cells.splice(newIdx, 0, ...cells);
|
||||
const changes: vscode.NotebookCellsChangeData[] = [{
|
||||
start: index,
|
||||
deletedCount: 1,
|
||||
deletedItems: cells.map(data => data.cell),
|
||||
items: []
|
||||
}, {
|
||||
start: newIdx,
|
||||
deletedCount: 0,
|
||||
deletedItems: [],
|
||||
items: cells.map(data => data.cell)
|
||||
}];
|
||||
this._emitter.emitModelChange({
|
||||
const changes = [
|
||||
new RawContentChangeEvent(index, 1, cells.map(c => c.cell), []),
|
||||
new RawContentChangeEvent(newIdx, 0, [], cells)
|
||||
];
|
||||
this._emitter.emitModelChange(deepFreeze({
|
||||
document: this.notebookDocument,
|
||||
changes
|
||||
});
|
||||
changes: RawContentChangeEvent.asApiEvents(changes)
|
||||
}));
|
||||
}
|
||||
|
||||
private _setCellOutputs(index: number, outputs: IOutputDto[]): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setOutputs(outputs);
|
||||
this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] });
|
||||
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] }));
|
||||
}
|
||||
|
||||
private _setCellOutputItems(index: number, outputId: string, append: boolean, outputItems: IOutputItemDto[]): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setOutputItems(outputId, append, outputItems);
|
||||
this._emitter.emitCellOutputsChange({ document: this.notebookDocument, cells: [cell.cell] });
|
||||
this._emitter.emitCellOutputsChange(deepFreeze({ document: this.notebookDocument, cells: [cell.cell] }));
|
||||
}
|
||||
|
||||
private _changeCellLanguage(index: number, language: string): void {
|
||||
private _changeCellLanguage(index: number, newModeId: string): void {
|
||||
const cell = this._cells[index];
|
||||
const event: vscode.NotebookCellLanguageChangeEvent = { document: this.notebookDocument, cell: cell.cell, language };
|
||||
this._emitter.emitCellLanguageChange(event);
|
||||
if (cell.cell.document.languageId !== newModeId) {
|
||||
this._textDocuments.$acceptModelModeChanged(cell.uri, newModeId);
|
||||
}
|
||||
}
|
||||
|
||||
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata | undefined): void {
|
||||
private _changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void {
|
||||
const cell = this._cells[index];
|
||||
cell.setMetadata(newMetadata || {});
|
||||
const event: vscode.NotebookCellMetadataChangeEvent = { document: this.notebookDocument, cell: cell.cell };
|
||||
this._emitter.emitCellMetadataChange(event);
|
||||
|
||||
const originalInternalMetadata = cell.internalMetadata;
|
||||
const originalExtMetadata = cell.cell.metadata;
|
||||
cell.setMetadata(newMetadata);
|
||||
const newExtMetadata = cell.cell.metadata;
|
||||
|
||||
if (!equals(originalExtMetadata, newExtMetadata)) {
|
||||
this._emitter.emitCellMetadataChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell }));
|
||||
}
|
||||
|
||||
if (originalInternalMetadata.runState !== newMetadata.runState) {
|
||||
const executionState = newMetadata.runState ?? extHostTypes.NotebookCellExecutionState.Idle;
|
||||
this._emitter.emitCellExecutionStateChange(deepFreeze({ document: this.notebookDocument, cell: cell.cell, executionState }));
|
||||
}
|
||||
}
|
||||
|
||||
getCellFromIndex(index: number): ExtHostCell | undefined {
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { readonly } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { MainThreadNotebookShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import * as extHostConverter from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { CellEditType, ICellEditOperation, ICellRange, ICellReplaceEdit, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, ICellEditOperation, ICellReplaceEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostNotebookDocument } from './extHostNotebookDocument';
|
||||
|
||||
@@ -46,7 +45,7 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
this._throwIfFinalized();
|
||||
this._collectedEdits.push({
|
||||
editType: CellEditType.DocumentMetadata,
|
||||
metadata: { ...notebookDocumentMetadataDefaults, ...value }
|
||||
metadata: value
|
||||
});
|
||||
}
|
||||
|
||||
@@ -85,28 +84,33 @@ class NotebookEditorCellEditBuilder implements vscode.NotebookEditorEdit {
|
||||
}
|
||||
|
||||
export class ExtHostNotebookEditor {
|
||||
private _selection?: vscode.NotebookCell;
|
||||
private _selections: vscode.NotebookCellRange[] = [];
|
||||
|
||||
private _visibleRanges: extHostTypes.NotebookCellRange[] = [];
|
||||
private _selections: vscode.NotebookCellRange[] = [];
|
||||
private _visibleRanges: vscode.NotebookCellRange[] = [];
|
||||
private _viewColumn?: vscode.ViewColumn;
|
||||
private _active: boolean = false;
|
||||
|
||||
private _visible: boolean = false;
|
||||
private _kernel?: vscode.NotebookKernel;
|
||||
|
||||
private _onDidDispose = new Emitter<void>();
|
||||
private readonly _hasDecorationsForKey = new Set<string>();
|
||||
private readonly _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose: Event<void> = this._onDidDispose.event;
|
||||
|
||||
private _hasDecorationsForKey: { [key: string]: boolean; } = Object.create(null);
|
||||
|
||||
private _editor: vscode.NotebookEditor | undefined;
|
||||
private _editor?: vscode.NotebookEditor;
|
||||
|
||||
constructor(
|
||||
readonly id: string,
|
||||
private readonly _viewType: string,
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
readonly notebookData: ExtHostNotebookDocument,
|
||||
visibleRanges: vscode.NotebookCellRange[],
|
||||
selections: vscode.NotebookCellRange[],
|
||||
viewColumn: vscode.ViewColumn | undefined
|
||||
) {
|
||||
this._selections = selections;
|
||||
this._visibleRanges = visibleRanges;
|
||||
this._viewColumn = viewColumn;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -122,7 +126,8 @@ export class ExtHostNotebookEditor {
|
||||
return that.notebookData.notebookDocument;
|
||||
},
|
||||
get selection() {
|
||||
return that._selection;
|
||||
const primarySelection = that._selections[0];
|
||||
return primarySelection && that.notebookData.getCellFromIndex(primarySelection.start)?.cell;
|
||||
},
|
||||
get selections() {
|
||||
return that._selections;
|
||||
@@ -131,7 +136,11 @@ export class ExtHostNotebookEditor {
|
||||
return that._visibleRanges;
|
||||
},
|
||||
revealRange(range, revealType) {
|
||||
that._proxy.$tryRevealRange(that.id, extHostConverter.NotebookCellRange.from(range), revealType ?? extHostTypes.NotebookEditorRevealType.Default);
|
||||
that._proxy.$tryRevealRange(
|
||||
that.id,
|
||||
extHostConverter.NotebookCellRange.from(range),
|
||||
revealType ?? extHostTypes.NotebookEditorRevealType.Default
|
||||
);
|
||||
},
|
||||
get viewColumn() {
|
||||
return that._viewColumn;
|
||||
@@ -163,34 +172,16 @@ export class ExtHostNotebookEditor {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
set visible(_state: boolean) {
|
||||
throw readonly('visible');
|
||||
}
|
||||
|
||||
_acceptVisibility(value: boolean) {
|
||||
this._visible = value;
|
||||
}
|
||||
|
||||
_acceptVisibleRanges(value: extHostTypes.NotebookCellRange[]): void {
|
||||
_acceptVisibleRanges(value: vscode.NotebookCellRange[]): void {
|
||||
this._visibleRanges = value;
|
||||
}
|
||||
|
||||
_acceptSelections(selections: ICellRange[]): void {
|
||||
const primarySelection = selections[0];
|
||||
this._selection = primarySelection ? this.notebookData.getCellFromIndex(primarySelection.start)?.cell : undefined;
|
||||
this._selections = selections.map(val => new extHostTypes.NotebookCellRange(val.start, val.end));
|
||||
}
|
||||
|
||||
get active(): boolean {
|
||||
return this._active;
|
||||
}
|
||||
|
||||
set active(_state: boolean) {
|
||||
throw readonly('active');
|
||||
}
|
||||
|
||||
_acceptActive(value: boolean) {
|
||||
this._active = value;
|
||||
_acceptSelections(selections: vscode.NotebookCellRange[]): void {
|
||||
this._selections = selections;
|
||||
}
|
||||
|
||||
private _applyEdit(editData: INotebookEditData): Promise<boolean> {
|
||||
@@ -213,9 +204,9 @@ export class ExtHostNotebookEditor {
|
||||
const prevIndex = compressedEditsIndex;
|
||||
const prev = compressedEdits[prevIndex];
|
||||
|
||||
if (prev.editType === CellEditType.Replace && editData.cellEdits[i].editType === CellEditType.Replace) {
|
||||
const edit = editData.cellEdits[i];
|
||||
if ((edit.editType !== CellEditType.DocumentMetadata) && prev.index === edit.index) {
|
||||
const edit = editData.cellEdits[i];
|
||||
if (prev.editType === CellEditType.Replace && edit.editType === CellEditType.Replace) {
|
||||
if (prev.index === edit.index) {
|
||||
prev.cells.push(...(editData.cellEdits[i] as ICellReplaceEdit).cells);
|
||||
prev.count += (editData.cellEdits[i] as ICellReplaceEdit).count;
|
||||
continue;
|
||||
@@ -230,15 +221,14 @@ export class ExtHostNotebookEditor {
|
||||
}
|
||||
|
||||
setDecorations(decorationType: vscode.NotebookEditorDecorationType, range: vscode.NotebookCellRange): void {
|
||||
const willBeEmpty = (range.start === range.end);
|
||||
if (willBeEmpty && !this._hasDecorationsForKey[decorationType.key]) {
|
||||
if (range.isEmpty && !this._hasDecorationsForKey.has(decorationType.key)) {
|
||||
// avoid no-op call to the renderer
|
||||
return;
|
||||
}
|
||||
if (willBeEmpty) {
|
||||
delete this._hasDecorationsForKey[decorationType.key];
|
||||
if (range.isEmpty) {
|
||||
this._hasDecorationsForKey.delete(decorationType.key);
|
||||
} else {
|
||||
this._hasDecorationsForKey[decorationType.key] = true;
|
||||
this._hasDecorationsForKey.add(decorationType.key);
|
||||
}
|
||||
|
||||
return this._proxy.$trySetDecorations(
|
||||
|
||||
@@ -25,7 +25,7 @@ export class ExtHostProgress implements ExtHostProgressShape {
|
||||
withProgress<R>(extension: IExtensionDescription, options: ProgressOptions, task: (progress: Progress<IProgressStep>, token: CancellationToken) => Thenable<R>): Thenable<R> {
|
||||
const handle = this._handles++;
|
||||
const { title, location, cancellable } = options;
|
||||
const source = localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name);
|
||||
const source = { label: localize('extensionSource', "{0} (Extension)", extension.displayName || extension.name), id: extension.identifier.value };
|
||||
|
||||
this._proxy.$startProgress(handle, { location: ProgressLocation.from(location), title, source, cancellable }, extension);
|
||||
return this._withProgress(handle, task, !!cancellable);
|
||||
|
||||
@@ -16,6 +16,7 @@ import { ThemeIcon, QuickInputButtons } from 'vs/workbench/api/common/extHostTyp
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
export type Item = string | QuickPickItem;
|
||||
|
||||
@@ -67,11 +68,12 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
const instance = ++this._instances;
|
||||
|
||||
const quickPickWidget = proxy.$show(instance, {
|
||||
placeHolder: options && options.placeHolder,
|
||||
matchOnDescription: options && options.matchOnDescription,
|
||||
matchOnDetail: options && options.matchOnDetail,
|
||||
ignoreFocusLost: options && options.ignoreFocusOut,
|
||||
canPickMany: options && options.canPickMany
|
||||
title: options?.title,
|
||||
placeHolder: options?.placeHolder,
|
||||
matchOnDescription: options?.matchOnDescription,
|
||||
matchOnDetail: options?.matchOnDetail,
|
||||
ignoreFocusLost: options?.ignoreFocusOut,
|
||||
canPickMany: options?.canPickMany,
|
||||
}, token);
|
||||
|
||||
const widgetClosedMarker = {};
|
||||
@@ -632,7 +634,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx
|
||||
|
||||
set validationMessage(validationMessage: string | undefined) {
|
||||
this._validationMessage = validationMessage;
|
||||
this.update({ validationMessage });
|
||||
this.update({ validationMessage, severity: validationMessage ? Severity.Error : Severity.Ignore });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
lib/vscode/src/vs/workbench/api/common/extHostTelemetry.ts
Normal file
31
lib/vscode/src/vs/workbench/api/common/extHostTelemetry.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTelemetryShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
export class ExtHostTelemetry implements ExtHostTelemetryShape {
|
||||
private readonly _onDidChangeTelemetryEnabled = new Emitter<boolean>();
|
||||
readonly onDidChangeTelemetryEnabled: Event<boolean> = this._onDidChangeTelemetryEnabled.event;
|
||||
|
||||
private _enabled: boolean = false;
|
||||
|
||||
getTelemetryEnabled(): boolean {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
$initializeTelemetryEnabled(enabled: boolean): void {
|
||||
this._enabled = enabled;
|
||||
}
|
||||
|
||||
$onDidChangeTelemetryEnabled(enabled: boolean): void {
|
||||
this._enabled = enabled;
|
||||
this._onDidChangeTelemetryEnabled.fire(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
export const IExtHostTelemetry = createDecorator<IExtHostTelemetry>('IExtHostTelemetry');
|
||||
export interface IExtHostTelemetry extends ExtHostTelemetry, ExtHostTelemetryShape { }
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IShellLaunchConfigDto, IShellAndArgsDto, ITerminalDimensionsDto, ITerminalLinkDto, TerminalIdentifier } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes';
|
||||
@@ -19,8 +19,9 @@ import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/ter
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
import { ITerminalChildProcess, ITerminalDimensionsOverride, ITerminalLaunchError, TerminalShellType } from 'vs/platform/terminal/common/terminal';
|
||||
import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering';
|
||||
import { ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
|
||||
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable {
|
||||
|
||||
@@ -133,7 +134,7 @@ export class ExtHostTerminal {
|
||||
if (typeof this._id !== 'string') {
|
||||
throw new Error('Terminal has already been created');
|
||||
}
|
||||
await this._proxy.$createTerminal(this._id, { name: this._name, isExtensionTerminal: true });
|
||||
await this._proxy.$createTerminal(this._id, { name: this._name, isExtensionCustomPtyTerminal: true });
|
||||
// At this point, the id has been set via `$acceptTerminalOpened`
|
||||
if (typeof this._id === 'string') {
|
||||
throw new Error('Terminal creation failed');
|
||||
@@ -198,6 +199,9 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
|
||||
public readonly onProcessTitleChanged: Event<string> = this._onProcessTitleChanged.event;
|
||||
private readonly _onProcessOverrideDimensions = new Emitter<ITerminalDimensionsOverride | undefined>();
|
||||
public get onProcessOverrideDimensions(): Event<ITerminalDimensionsOverride | undefined> { return this._onProcessOverrideDimensions.event; }
|
||||
private readonly _onProcessShellTypeChanged = new Emitter<TerminalShellType>();
|
||||
public readonly onProcessShellTypeChanged = this._onProcessShellTypeChanged.event;
|
||||
|
||||
|
||||
constructor(private readonly _pty: vscode.Pseudoterminal) { }
|
||||
|
||||
@@ -324,8 +328,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I
|
||||
public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal;
|
||||
public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
|
||||
public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
|
||||
public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
|
||||
public abstract $getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||
public abstract $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]>;
|
||||
public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||
|
||||
@@ -776,11 +779,7 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined> {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
public $getAvailableShells(): Promise<IShellDefinitionDto[]> {
|
||||
public $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]> {
|
||||
throw new NotSupportedError();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
|
||||
import { mapFind } from 'vs/base/common/arrays';
|
||||
import { disposableTimeout } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { once } from 'vs/base/common/functional';
|
||||
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { deepFreeze } from 'vs/base/common/objects';
|
||||
import { isDefined } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
@@ -17,11 +17,11 @@ import { ExtHostTestingResource, ExtHostTestingShape, MainContext, MainThreadTes
|
||||
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { TestItem, TestResults, TestState } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Disposable } from 'vs/workbench/api/common/extHostTypes';
|
||||
import * as Convert from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { Disposable, TestItem as TestItemImpl, TestItemHookProperty } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { OwnedTestCollection, SingleUseTestCollection, TestPosition } from 'vs/workbench/contrib/testing/common/ownedTestCollection';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, RunTestForProviderRequest, TestDiffOpType, TestIdWithProvider, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { AbstractIncrementalTestCollection, IncrementalChangeCollector, IncrementalTestCollectionItem, InternalTestItem, ISerializedTestResults, RunTestForProviderRequest, TestDiffOpType, TestIdWithSrc, TestsDiff } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
const getTestSubscriptionKey = (resource: ExtHostTestingResource, uri: URI) => `${resource}:${uri.toString()}`;
|
||||
@@ -93,7 +93,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
tests
|
||||
.map(this.getInternalTestForReference, this)
|
||||
.filter(isDefined)
|
||||
.map(t => ({ providerId: t.providerId, testId: t.item.extId }));
|
||||
.map(t => ({ src: t.src, testId: t.item.extId }));
|
||||
|
||||
await this.proxy.$runTests({
|
||||
exclude: req.exclude ? testListToProviders(req.exclude).map(t => t.testId) : undefined,
|
||||
@@ -106,7 +106,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
* Implements vscode.test.publishTestResults
|
||||
*/
|
||||
public publishExtensionProvidedResults(results: vscode.TestResults, persist: boolean): void {
|
||||
this.proxy.$publishExtensionProvidedResults(TestResults.from(generateUuid(), results), persist);
|
||||
this.proxy.$publishExtensionProvidedResults(Convert.TestResults.from(generateUuid(), results), persist);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +116,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
public $publishTestResults(results: ISerializedTestResults[]): void {
|
||||
this.results = Object.freeze(
|
||||
results
|
||||
.map(r => deepFreeze(TestResults.to(r)))
|
||||
.map(r => deepFreeze(Convert.TestResults.to(r)))
|
||||
.concat(this.results)
|
||||
.sort((a, b) => b.completedAt - a.completedAt)
|
||||
.slice(0, 32),
|
||||
@@ -136,7 +136,8 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
return;
|
||||
}
|
||||
|
||||
let method: undefined | ((p: vscode.TestProvider) => vscode.TestHierarchy<vscode.TestItem> | undefined);
|
||||
const cancellation = new CancellationTokenSource();
|
||||
let method: undefined | ((p: vscode.TestProvider) => vscode.ProviderResult<vscode.TestItem>);
|
||||
if (resource === ExtHostTestingResource.TextDocument) {
|
||||
let document = this.documents.getDocument(uri);
|
||||
|
||||
@@ -155,14 +156,14 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
|
||||
if (document) {
|
||||
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
|
||||
method = p => p.createDocumentTestHierarchy
|
||||
? p.createDocumentTestHierarchy(document!.document)
|
||||
: this.createDefaultDocumentTestHierarchy(p, document!.document, folder);
|
||||
method = p => p.provideDocumentTestRoot
|
||||
? p.provideDocumentTestRoot(document!.document, cancellation.token)
|
||||
: createDefaultDocumentTestRoot(p, document!.document, folder, cancellation.token);
|
||||
}
|
||||
} else {
|
||||
const folder = await this.workspace.getWorkspaceFolder2(uri, false);
|
||||
if (folder) {
|
||||
method = p => p.createWorkspaceTestHierarchy?.(folder);
|
||||
method = p => p.provideWorkspaceTestRoot(folder, cancellation.token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,33 +171,21 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
return;
|
||||
}
|
||||
|
||||
const subscribeFn = (id: string, provider: vscode.TestProvider) => {
|
||||
const subscribeFn = async (id: string, provider: vscode.TestProvider) => {
|
||||
try {
|
||||
const hierarchy = method!(provider);
|
||||
if (!hierarchy) {
|
||||
return;
|
||||
const root = await method!(provider);
|
||||
if (root) {
|
||||
collection.addRoot(root, id);
|
||||
}
|
||||
|
||||
collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, 1]);
|
||||
disposable.add(hierarchy);
|
||||
collection.addRoot(hierarchy.root, id);
|
||||
Promise.resolve(hierarchy.discoveredInitialTests).then(() => collection.pushDiff([TestDiffOpType.DeltaDiscoverComplete, -1]));
|
||||
hierarchy.onDidChangeTest(e => collection.onItemChange(e, id));
|
||||
hierarchy.onDidInvalidateTest?.(e => {
|
||||
const internal = collection.getTestByReference(e);
|
||||
if (!internal) {
|
||||
console.warn(`Received a TestProvider.onDidInvalidateTest for a test that does not currently exist.`);
|
||||
} else {
|
||||
this.proxy.$retireTest(internal.item.extId);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
const disposable = new DisposableStore();
|
||||
const collection = disposable.add(this.ownedTests.createForHierarchy(diff => this.proxy.$publishDiff(resource, uriComponents, diff)));
|
||||
const collection = disposable.add(this.ownedTests.createForHierarchy(
|
||||
diff => this.proxy.$publishDiff(resource, uriComponents, diff)));
|
||||
disposable.add(toDisposable(() => cancellation.dispose(true)));
|
||||
for (const [id, provider] of this.providers) {
|
||||
subscribeFn(id, provider);
|
||||
}
|
||||
@@ -208,6 +197,17 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
this.testSubscriptions.set(subscriptionKey, { store: disposable, collection, subscribeFn });
|
||||
}
|
||||
|
||||
/**
|
||||
* Expands the nodes in the test tree. If levels is less than zero, it will
|
||||
* be treated as infinite.
|
||||
* @override
|
||||
*/
|
||||
public async $expandTest(test: TestIdWithSrc, levels: number) {
|
||||
const sub = mapFind(this.testSubscriptions.values(), s => s.collection.treeId === test.src.tree ? s : undefined);
|
||||
await sub?.collection.expand(test.testId, levels < 0 ? Infinity : levels);
|
||||
this.flushCollectionDiffs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of a previous subscription to tests.
|
||||
* @override
|
||||
@@ -238,12 +238,16 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
* @override
|
||||
*/
|
||||
public async $runTestsForProvider(req: RunTestForProviderRequest, cancellation: CancellationToken): Promise<void> {
|
||||
const provider = this.providers.get(req.providerId);
|
||||
if (!provider || !provider.runTests) {
|
||||
const provider = this.providers.get(req.tests[0].src.provider);
|
||||
if (!provider) {
|
||||
return;
|
||||
}
|
||||
|
||||
const includeTests = req.ids.map(id => this.ownedTests.getTestById(id)?.[1]).filter(isDefined);
|
||||
const includeTests = req.tests
|
||||
.map(({ testId, src }) => this.ownedTests.getTestById(testId, src.tree))
|
||||
.filter(isDefined)
|
||||
.map(([_tree, test]) => test);
|
||||
|
||||
const excludeTests = req.excludeExtIds
|
||||
.map(id => this.ownedTests.getTestById(id))
|
||||
.filter(isDefined)
|
||||
@@ -268,7 +272,7 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
}
|
||||
|
||||
this.flushCollectionDiffs();
|
||||
this.proxy.$updateTestStateInRun(req.runId, test.id, TestState.from(state));
|
||||
this.proxy.$updateTestStateInRun(req.runId, test.id, Convert.TestState.from(state));
|
||||
},
|
||||
tests: includeTests.map(t => TestItemFilteredWrapper.unwrap(t.actual)),
|
||||
exclude: excludeTests.map(([, t]) => TestItemFilteredWrapper.unwrap(t.actual)),
|
||||
@@ -286,13 +290,13 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
}
|
||||
}
|
||||
|
||||
public $lookupTest(req: TestIdWithProvider): Promise<InternalTestItem | undefined> {
|
||||
public $lookupTest(req: TestIdWithSrc): Promise<InternalTestItem | undefined> {
|
||||
const owned = this.ownedTests.getTestById(req.testId);
|
||||
if (!owned) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
const { actual, previousChildren, previousEquals, ...item } = owned[1];
|
||||
const { actual, discoverCts, expandLevels, ...item } = owned[1];
|
||||
return Promise.resolve(item);
|
||||
}
|
||||
|
||||
@@ -317,95 +321,54 @@ export class ExtHostTesting implements ExtHostTestingShape {
|
||||
?? mapFind(this.testSubscriptions.values(), c => c.collection.getTestByReference(test))
|
||||
?? this.textDocumentObservers.getMirroredTestDataByReference(test);
|
||||
}
|
||||
|
||||
private createDefaultDocumentTestHierarchy(provider: vscode.TestProvider, document: vscode.TextDocument, folder: vscode.WorkspaceFolder | undefined): vscode.TestHierarchy<vscode.TestItem> | undefined {
|
||||
if (!folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workspaceHierarchy = provider.createWorkspaceTestHierarchy?.(folder);
|
||||
if (!workspaceHierarchy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const onDidInvalidateTest = new Emitter<vscode.TestItem>();
|
||||
workspaceHierarchy.onDidInvalidateTest?.(node => {
|
||||
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(node, document);
|
||||
if (wrapper.hasNodeMatchingFilter) {
|
||||
onDidInvalidateTest.fire(wrapper);
|
||||
}
|
||||
});
|
||||
|
||||
const onDidChangeTest = new Emitter<vscode.TestItem>();
|
||||
workspaceHierarchy.onDidChangeTest(node => {
|
||||
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(node, document);
|
||||
const previouslySeen = wrapper.hasNodeMatchingFilter;
|
||||
|
||||
if (previouslySeen) {
|
||||
// reset cache and get whether you can currently see the TestItem.
|
||||
wrapper.reset();
|
||||
const currentlySeen = wrapper.hasNodeMatchingFilter;
|
||||
|
||||
if (currentlySeen) {
|
||||
onDidChangeTest.fire(wrapper);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fire the event to say that the current visible parent has changed.
|
||||
onDidChangeTest.fire(wrapper.visibleParent);
|
||||
return;
|
||||
}
|
||||
|
||||
const previousParent = wrapper.visibleParent;
|
||||
wrapper.reset();
|
||||
const currentlySeen = wrapper.hasNodeMatchingFilter;
|
||||
|
||||
// It wasn't previously seen and isn't currently seen so
|
||||
// nothing has actually changed.
|
||||
if (!currentlySeen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The test is now visible so we need to refresh the cache
|
||||
// of the previous visible parent and fire that it has changed.
|
||||
previousParent.reset();
|
||||
onDidChangeTest.fire(previousParent);
|
||||
});
|
||||
|
||||
return {
|
||||
root: TestItemFilteredWrapper.getWrapperForTestItem(workspaceHierarchy.root, document),
|
||||
dispose: () => {
|
||||
onDidChangeTest.dispose();
|
||||
TestItemFilteredWrapper.removeFilter(document);
|
||||
},
|
||||
discoveredInitialTests: workspaceHierarchy.discoveredInitialTests,
|
||||
onDidInvalidateTest: onDidInvalidateTest.event,
|
||||
onDidChangeTest: onDidChangeTest.event
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const createDefaultDocumentTestRoot = async <T extends vscode.TestItem>(
|
||||
provider: vscode.TestProvider<T>,
|
||||
document: vscode.TextDocument,
|
||||
folder: vscode.WorkspaceFolder | undefined,
|
||||
token: CancellationToken,
|
||||
) => {
|
||||
if (!folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const root = await provider.provideWorkspaceTestRoot(folder, token);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
token.onCancellationRequested(() => {
|
||||
TestItemFilteredWrapper.removeFilter(document);
|
||||
});
|
||||
|
||||
return TestItemFilteredWrapper.getWrapperForTestItem(root, document);
|
||||
};
|
||||
|
||||
/*
|
||||
* A class which wraps a vscode.TestItem that provides the ability to filter a TestItem's children
|
||||
* to only the children that are located in a certain vscode.Uri.
|
||||
*/
|
||||
export class TestItemFilteredWrapper implements vscode.TestItem {
|
||||
export class TestItemFilteredWrapper<T extends vscode.TestItem = vscode.TestItem> extends TestItemImpl {
|
||||
private static wrapperMap = new WeakMap<vscode.TextDocument, WeakMap<vscode.TestItem, TestItemFilteredWrapper>>();
|
||||
public static removeFilter(document: vscode.TextDocument): void {
|
||||
this.wrapperMap.delete(document);
|
||||
}
|
||||
|
||||
// Wraps the TestItem specified in a TestItemFilteredWrapper and pulls from a cache if it already exists.
|
||||
public static getWrapperForTestItem(item: vscode.TestItem, filterDocument: vscode.TextDocument, parent?: TestItemFilteredWrapper): TestItemFilteredWrapper {
|
||||
public static getWrapperForTestItem<T extends vscode.TestItem>(
|
||||
item: T,
|
||||
filterDocument: vscode.TextDocument,
|
||||
parent?: TestItemFilteredWrapper<T>,
|
||||
): TestItemFilteredWrapper<T> {
|
||||
let innerMap = this.wrapperMap.get(filterDocument);
|
||||
if (innerMap?.has(item)) {
|
||||
return innerMap.get(item)!;
|
||||
return innerMap.get(item) as TestItemFilteredWrapper<T>;
|
||||
}
|
||||
|
||||
if (!innerMap) {
|
||||
innerMap = new WeakMap<vscode.TestItem, TestItemFilteredWrapper>();
|
||||
this.wrapperMap.set(filterDocument, innerMap);
|
||||
|
||||
}
|
||||
|
||||
const w = new TestItemFilteredWrapper(item, filterDocument, parent);
|
||||
@@ -413,75 +376,72 @@ export class TestItemFilteredWrapper implements vscode.TestItem {
|
||||
return w;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the TestItem is wrapped, returns the unwrapped item provided
|
||||
* by the extension.
|
||||
*/
|
||||
public static unwrap(item: vscode.TestItem) {
|
||||
return item instanceof TestItemFilteredWrapper ? item.actual : item;
|
||||
}
|
||||
|
||||
public get id() {
|
||||
return this.actual.id;
|
||||
}
|
||||
private _cachedMatchesFilter: boolean | undefined;
|
||||
|
||||
public get label() {
|
||||
return this.actual.label;
|
||||
}
|
||||
|
||||
public get debuggable() {
|
||||
return this.actual.debuggable;
|
||||
}
|
||||
|
||||
public get description() {
|
||||
return this.actual.description;
|
||||
}
|
||||
|
||||
public get location() {
|
||||
return this.actual.location;
|
||||
}
|
||||
|
||||
public get runnable() {
|
||||
return this.actual.runnable;
|
||||
}
|
||||
|
||||
public get children() {
|
||||
// We only want children that match the filter.
|
||||
return this.getWrappedChildren().filter(child => child.hasNodeMatchingFilter);
|
||||
}
|
||||
|
||||
public get visibleParent(): TestItemFilteredWrapper {
|
||||
return this.hasNodeMatchingFilter ? this : this.parent!.visibleParent;
|
||||
}
|
||||
|
||||
private matchesFilter: boolean | undefined;
|
||||
|
||||
// Determines if the TestItem matches the filter. This would be true if:
|
||||
// 1. We don't have a parent (because the root is the workspace root node)
|
||||
// 2. The URI of the current node matches the filter URI
|
||||
// 3. Some child of the current node matches the filter URI
|
||||
/**
|
||||
* Gets whether this node, or any of its children, match the document filter.
|
||||
*/
|
||||
public get hasNodeMatchingFilter(): boolean {
|
||||
if (this.matchesFilter === undefined) {
|
||||
this.matchesFilter = !this.parent
|
||||
|| this.actual.location?.uri.toString() === this.filterDocument.uri.toString()
|
||||
|| this.getWrappedChildren().some(child => child.hasNodeMatchingFilter);
|
||||
if (this._cachedMatchesFilter === undefined) {
|
||||
return this.refreshMatch();
|
||||
} else {
|
||||
return this._cachedMatchesFilter;
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(
|
||||
public readonly actual: T,
|
||||
private filterDocument: vscode.TextDocument,
|
||||
public readonly parent?: TestItemFilteredWrapper<T>,
|
||||
) {
|
||||
super(actual.id, actual.label, actual.uri, actual.expandable);
|
||||
if (!(actual instanceof TestItemImpl)) {
|
||||
throw new Error(`TestItems provided to the VS Code API must extend \`vscode.TestItem\`, but ${actual.id} did not`);
|
||||
}
|
||||
|
||||
return this.matchesFilter;
|
||||
(actual as TestItemImpl)[TestItemHookProperty] = {
|
||||
setProp: (key, value) => (this as Record<string, unknown>)[key] = value,
|
||||
created: child => TestItemFilteredWrapper.getWrapperForTestItem(child, this.filterDocument, this).refreshMatch(),
|
||||
invalidate: () => this.invalidate(),
|
||||
delete: child => this.children.delete(child),
|
||||
};
|
||||
}
|
||||
|
||||
// Reset the cache of whether or not you can see a node from a particular node
|
||||
// up to it's visible parent.
|
||||
public reset(): void {
|
||||
if (this !== this.visibleParent) {
|
||||
this.parent?.reset();
|
||||
/**
|
||||
* Refreshes the `hasNodeMatchingFilter` state for this item. It matches
|
||||
* if the test itself has a location that matches, or if any of its
|
||||
* children do.
|
||||
*/
|
||||
private refreshMatch() {
|
||||
const didMatch = this._cachedMatchesFilter;
|
||||
|
||||
// The `children` of the wrapper only include the children who match the
|
||||
// filter. Synchronize them.
|
||||
for (const rawChild of this.actual.children) {
|
||||
const wrapper = TestItemFilteredWrapper.getWrapperForTestItem(rawChild, this.filterDocument, this);
|
||||
if (wrapper.hasNodeMatchingFilter) {
|
||||
this.children.add(wrapper);
|
||||
} else {
|
||||
this.children.delete(wrapper);
|
||||
}
|
||||
}
|
||||
this.matchesFilter = undefined;
|
||||
}
|
||||
|
||||
const nowMatches = this.children.size > 0 || this.actual.uri.toString() === this.filterDocument.uri.toString();
|
||||
this._cachedMatchesFilter = nowMatches;
|
||||
|
||||
private constructor(public readonly actual: vscode.TestItem, private filterDocument: vscode.TextDocument, private readonly parent?: TestItemFilteredWrapper) {
|
||||
this.getWrappedChildren();
|
||||
}
|
||||
if (nowMatches !== didMatch) {
|
||||
this.parent?.refreshMatch();
|
||||
}
|
||||
|
||||
private getWrappedChildren() {
|
||||
return this.actual.children?.map(t => TestItemFilteredWrapper.getWrapperForTestItem(t, this.filterDocument, this)) || [];
|
||||
return this._cachedMatchesFilter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,7 +451,6 @@ export class TestItemFilteredWrapper implements vscode.TestItem {
|
||||
interface MirroredCollectionTestItem extends IncrementalTestCollectionItem {
|
||||
revived: vscode.TestItem;
|
||||
depth: number;
|
||||
wrapped?: vscode.RequiredTestItem;
|
||||
}
|
||||
|
||||
class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollectionTestItem> {
|
||||
@@ -505,7 +464,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
return this.added.size === 0 && this.removed.size === 0 && this.updated.size === 0;
|
||||
}
|
||||
|
||||
constructor(private readonly collection: MirroredTestCollection, private readonly emitter: Emitter<vscode.TestChangeEvent>) {
|
||||
constructor(private readonly emitter: Emitter<vscode.TestChangeEvent>) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -520,7 +479,7 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
* @override
|
||||
*/
|
||||
public update(node: MirroredCollectionTestItem): void {
|
||||
Object.assign(node.revived, TestItem.toShallow(node.item));
|
||||
Object.assign(node.revived, Convert.TestItem.toPlain(node.item));
|
||||
if (!this.added.has(node)) {
|
||||
this.updated.add(node);
|
||||
}
|
||||
@@ -549,79 +508,11 @@ class MirroredChangeCollector extends IncrementalChangeCollector<MirroredCollect
|
||||
* @override
|
||||
*/
|
||||
public getChangeEvent(): vscode.TestChangeEvent {
|
||||
const { collection, added, updated, removed } = this;
|
||||
const { added, updated, removed } = this;
|
||||
return {
|
||||
get added() { return [...added].map(collection.getPublicTestItem, collection); },
|
||||
get updated() { return [...updated].map(collection.getPublicTestItem, collection); },
|
||||
get removed() { return [...removed].map(collection.getPublicTestItem, collection); },
|
||||
get commonChangeAncestor() {
|
||||
let ancestorPath: MirroredCollectionTestItem[] | undefined;
|
||||
const buildAncestorPath = (node: MirroredCollectionTestItem | undefined) => {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// add the node and all its parents to the list of ancestors. If
|
||||
// the node is detached, do not return a path (its parent will
|
||||
// also have been passed to remove() and be present)
|
||||
const path: MirroredCollectionTestItem[] = new Array(node.depth + 1);
|
||||
for (let i = node.depth; i >= 0; i--) {
|
||||
if (!node) {
|
||||
return undefined; // detached child
|
||||
}
|
||||
|
||||
path[node.depth] = node;
|
||||
node = node.parent ? collection.getMirroredTestDataById(node.parent) : undefined;
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
const addAncestorPath = (node: MirroredCollectionTestItem) => {
|
||||
// fast path: if the common ancestor is already the root, no more work to do
|
||||
if (ancestorPath && ancestorPath.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const thisPath = buildAncestorPath(node);
|
||||
if (!thisPath) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ancestorPath) {
|
||||
ancestorPath = thisPath;
|
||||
return;
|
||||
}
|
||||
|
||||
// removes node from the path to the ancestor that don't match
|
||||
// the corresponding node in *this* path.
|
||||
for (let i = ancestorPath.length - 1; i >= 0; i--) {
|
||||
if (ancestorPath[i] !== thisPath[i]) {
|
||||
ancestorPath.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const addParentAncestor = (node: MirroredCollectionTestItem) => {
|
||||
if (ancestorPath && ancestorPath.length === 0) {
|
||||
// no-op
|
||||
} else if (node.parent === null) {
|
||||
ancestorPath = [];
|
||||
} else {
|
||||
const parent = collection.getMirroredTestDataById(node.parent);
|
||||
if (parent) {
|
||||
addAncestorPath(parent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (const node of added) { addParentAncestor(node); }
|
||||
for (const node of updated) { addAncestorPath(node); }
|
||||
for (const node of removed) { addParentAncestor(node); }
|
||||
|
||||
const ancestor = ancestorPath && ancestorPath[ancestorPath.length - 1];
|
||||
return ancestor ? collection.getPublicTestItem(ancestor) : null;
|
||||
},
|
||||
get added() { return [...added].map(n => n.revived); },
|
||||
get updated() { return [...updated].map(n => n.revived); },
|
||||
get removed() { return [...removed].map(n => n.revived); },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -654,12 +545,12 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
|
||||
/**
|
||||
* Translates the item IDs to TestItems for exposure to extensions.
|
||||
*/
|
||||
public getAllAsTestItem(itemIds: Iterable<string>): vscode.RequiredTestItem[] {
|
||||
let output: vscode.RequiredTestItem[] = [];
|
||||
public getAllAsTestItem(itemIds: Iterable<string>) {
|
||||
let output: vscode.TestItem[] = [];
|
||||
for (const itemId of itemIds) {
|
||||
const item = this.items.get(itemId);
|
||||
if (item) {
|
||||
output.push(this.getPublicTestItem(item));
|
||||
output.push(item.revived);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,64 +576,23 @@ export class MirroredTestCollection extends AbstractIncrementalTestCollection<Mi
|
||||
* @override
|
||||
*/
|
||||
protected createItem(item: InternalTestItem, parent?: MirroredCollectionTestItem): MirroredCollectionTestItem {
|
||||
return { ...item, revived: TestItem.toShallow(item.item), depth: parent ? parent.depth + 1 : 0, children: new Set() };
|
||||
return {
|
||||
...item,
|
||||
// todo@connor4312: make this work well again with children
|
||||
revived: Convert.TestItem.toPlain(item.item) as vscode.TestItem,
|
||||
depth: parent ? parent.depth + 1 : 0,
|
||||
children: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
protected createChangeCollector() {
|
||||
return new MirroredChangeCollector(this, this.changeEmitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the public test item instance for the given mirrored record.
|
||||
*/
|
||||
public getPublicTestItem(item: MirroredCollectionTestItem): vscode.RequiredTestItem {
|
||||
if (!item.wrapped) {
|
||||
item.wrapped = new TestItemFromMirror(item, this);
|
||||
}
|
||||
|
||||
return item.wrapped;
|
||||
return new MirroredChangeCollector(this.changeEmitter);
|
||||
}
|
||||
}
|
||||
|
||||
class TestItemFromMirror implements vscode.RequiredTestItem {
|
||||
readonly #internal: MirroredCollectionTestItem;
|
||||
readonly #collection: MirroredTestCollection;
|
||||
|
||||
public get id() { return this.#internal.revived.id!; }
|
||||
public get label() { return this.#internal.revived.label; }
|
||||
public get description() { return this.#internal.revived.description; }
|
||||
public get location() { return this.#internal.revived.location; }
|
||||
public get runnable() { return this.#internal.revived.runnable ?? true; }
|
||||
public get debuggable() { return this.#internal.revived.debuggable ?? false; }
|
||||
public get children() {
|
||||
return this.#collection.getAllAsTestItem(this.#internal.children);
|
||||
}
|
||||
|
||||
constructor(internal: MirroredCollectionTestItem, collection: MirroredTestCollection) {
|
||||
this.#internal = internal;
|
||||
this.#collection = collection;
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
const serialized: vscode.RequiredTestItem & TestIdWithProvider = {
|
||||
id: this.id,
|
||||
label: this.label,
|
||||
description: this.description,
|
||||
location: this.location,
|
||||
runnable: this.runnable,
|
||||
debuggable: this.debuggable,
|
||||
children: this.children.map(c => (c as TestItemFromMirror).toJSON()),
|
||||
|
||||
providerId: this.#internal.providerId,
|
||||
testId: this.id,
|
||||
};
|
||||
|
||||
return serialized;
|
||||
}
|
||||
}
|
||||
|
||||
interface IObserverData {
|
||||
observers: number;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as vscode from 'vscode';
|
||||
import { RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { ProvidedPortAttributes, RemoteTunnel, TunnelCreationOptions, TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
@@ -46,6 +46,7 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
|
||||
getTunnels(): Promise<vscode.TunnelDescription[]>;
|
||||
onDidChangeTunnels: vscode.Event<void>;
|
||||
setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable>;
|
||||
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider): IDisposable;
|
||||
}
|
||||
|
||||
export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IExtHostTunnelService');
|
||||
@@ -71,6 +72,14 @@ export class ExtHostTunnelService implements IExtHostTunnelService {
|
||||
async setTunnelExtensionFunctions(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
return { dispose: () => { } };
|
||||
}
|
||||
registerPortsAttributesProvider(portSelector: { pid?: number, portRange?: [number, number] }, provider: vscode.PortAttributesProvider) {
|
||||
return { dispose: () => { } };
|
||||
}
|
||||
|
||||
async $providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: vscode.CancellationToken): Promise<ProvidedPortAttributes[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto | undefined> { return undefined; }
|
||||
async $closeTunnel(remote: { host: string, port: number }): Promise<void> { }
|
||||
async $onDidTunnelsChange(): Promise<void> { }
|
||||
|
||||
@@ -3,36 +3,35 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as types from './extHostTypes';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { ITextEditorOptions, EditorOverride } from 'vs/platform/editor/common/editor';
|
||||
import { IDecorationOptions, IThemeDecorationRenderOptions, IDecorationRenderOptions, IContentDecorationRenderOptions } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import type * as vscode from 'vscode';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as editorRange from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import * as htmlContent from 'vs/base/common/htmlContent';
|
||||
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MarkerSeverity, IRelatedInformation, IMarkerData, MarkerTag } from 'vs/platform/markers/common/markers';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { isString, isNumber, isDefined } from 'vs/base/common/types';
|
||||
import * as marked from 'vs/base/common/marked/marked';
|
||||
import { parse } from 'vs/base/common/marshalling';
|
||||
import { cloneAndChange } from 'vs/base/common/objects';
|
||||
import { LogLevel as _MainLogLevel } from 'vs/platform/log/common/log';
|
||||
import { coalesce, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { isDefined, isNumber, isString } from 'vs/base/common/types';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
|
||||
import { IPosition } from 'vs/editor/common/core/position';
|
||||
import * as editorRange from 'vs/editor/common/core/range';
|
||||
import { ISelection } from 'vs/editor/common/core/selection';
|
||||
import { IContentDecorationRenderOptions, IDecorationOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions } from 'vs/editor/common/editorCommon';
|
||||
import { EndOfLineSequence, TrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as languageSelector from 'vs/editor/common/modes/languageSelector';
|
||||
import { EditorOverride, ITextEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
|
||||
import { ProgressLocation as MainProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook';
|
||||
import { EditorGroupColumn, SaveReason } from 'vs/workbench/common/editor';
|
||||
import * as notebooks from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { ISerializedTestResults, ITestItem, ITestState, SerializedTestResultItem } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { ISerializedTestResults, ITestItem, ITestMessage, ITestState, SerializedTestResultItem, TestItemExpandState } from 'vs/workbench/contrib/testing/common/testCollection';
|
||||
import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as types from './extHostTypes';
|
||||
|
||||
export interface PositionLike {
|
||||
line: number;
|
||||
@@ -568,7 +567,7 @@ export namespace WorkspaceEdit {
|
||||
metadata: entry.metadata,
|
||||
resource: entry.uri,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.Metadata,
|
||||
editType: notebooks.CellEditType.PartialMetadata,
|
||||
index: entry.index,
|
||||
metadata: entry.newMetadata
|
||||
}
|
||||
@@ -594,7 +593,6 @@ export namespace WorkspaceEdit {
|
||||
resource: entry.uri,
|
||||
edit: {
|
||||
editType: notebooks.CellEditType.OutputItems,
|
||||
index: entry.index,
|
||||
outputId: entry.outputId,
|
||||
items: entry.newOutputItems?.map(NotebookCellOutputItem.from) || [],
|
||||
append: entry.append
|
||||
@@ -790,8 +788,8 @@ export namespace location {
|
||||
};
|
||||
}
|
||||
|
||||
export function to(value: modes.Location): types.Location {
|
||||
return new types.Location(value.uri, Range.to(value.range));
|
||||
export function to(value: extHostProtocol.ILocationDto): types.Location {
|
||||
return new types.Location(URI.revive(value.uri), Range.to(value.range));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -810,9 +808,9 @@ export namespace DefinitionLink {
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
export function to(value: modes.LocationLink): vscode.LocationLink {
|
||||
export function to(value: extHostProtocol.IDefinitionLinkDto): vscode.LocationLink {
|
||||
return {
|
||||
targetUri: value.uri,
|
||||
targetUri: URI.revive(value.uri),
|
||||
targetRange: Range.to(value.range),
|
||||
targetSelectionRange: value.targetSelectionRange
|
||||
? Range.to(value.targetSelectionRange)
|
||||
@@ -901,12 +899,13 @@ export namespace InlineValue {
|
||||
export namespace InlineValueContext {
|
||||
export function from(inlineValueContext: vscode.InlineValueContext): extHostProtocol.IInlineValueContextDto {
|
||||
return <extHostProtocol.IInlineValueContextDto>{
|
||||
frameId: inlineValueContext.frameId,
|
||||
stoppedLocation: Range.from(inlineValueContext.stoppedLocation)
|
||||
};
|
||||
}
|
||||
|
||||
export function to(inlineValueContext: extHostProtocol.IInlineValueContextDto): types.InlineValueContext {
|
||||
return new types.InlineValueContext(Range.to(inlineValueContext.stoppedLocation));
|
||||
return new types.InlineValueContext(inlineValueContext.frameId, Range.to(inlineValueContext.stoppedLocation));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1419,7 +1418,7 @@ export namespace NotebookCellRange {
|
||||
export namespace NotebookCellMetadata {
|
||||
|
||||
export function to(data: notebooks.NotebookCellMetadata): types.NotebookCellMetadata {
|
||||
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.runnable, data.hasExecutionOrder, data.executionOrder, data.runState, data.runStartTime, data.statusMessage, data.lastRunDuration, data.inputCollapsed, data.outputCollapsed, data.custom);
|
||||
return new types.NotebookCellMetadata(data.editable, data.breakpointMargin, data.hasExecutionOrder, data.statusMessage, data.inputCollapsed, data.outputCollapsed, data.custom);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1430,9 +1429,26 @@ export namespace NotebookDocumentMetadata {
|
||||
}
|
||||
|
||||
export function to(data: notebooks.NotebookDocumentMetadata): types.NotebookDocumentMetadata {
|
||||
return new types.NotebookDocumentMetadata(data.editable, data.runnable, data.cellEditable, data.cellRunnable, data.cellHasExecutionOrder, data.displayOrder, data.custom, data.runState, data.trusted);
|
||||
return new types.NotebookDocumentMetadata(data.editable, data.cellEditable, data.cellHasExecutionOrder, data.custom, data.trusted);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellPreviousExecutionResult {
|
||||
export function to(data: notebooks.NotebookCellMetadata): vscode.NotebookCellExecutionSummary {
|
||||
return {
|
||||
duration: data.lastRunDuration,
|
||||
executionOrder: data.executionOrder,
|
||||
success: data.lastRunSuccess
|
||||
};
|
||||
}
|
||||
|
||||
export function from(data: vscode.NotebookCellExecutionSummary): Partial<notebooks.NotebookCellMetadata> {
|
||||
return {
|
||||
lastRunSuccess: data.success,
|
||||
lastRunDuration: data.duration,
|
||||
executionOrder: data.executionOrder
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellKind {
|
||||
@@ -1461,21 +1477,26 @@ export namespace NotebookCellData {
|
||||
|
||||
export function from(data: vscode.NotebookCellData): notebooks.ICellDto2 {
|
||||
return {
|
||||
cellKind: NotebookCellKind.from(data.cellKind),
|
||||
cellKind: NotebookCellKind.from(data.kind),
|
||||
language: data.language,
|
||||
source: data.source,
|
||||
metadata: data.metadata,
|
||||
outputs: data.outputs.map(output => ({
|
||||
outputId: output.id,
|
||||
metadata: output.metadata,
|
||||
outputs: (output.outputs || []).map(op => ({
|
||||
mime: op.mime,
|
||||
value: op.value,
|
||||
metadata: op.metadata
|
||||
}))
|
||||
}))
|
||||
metadata: {
|
||||
...data.metadata,
|
||||
...NotebookCellPreviousExecutionResult.from(data.latestExecutionSummary ?? {})
|
||||
},
|
||||
outputs: data.outputs ? data.outputs.map(NotebookCellOutput.from) : []
|
||||
};
|
||||
}
|
||||
|
||||
export function to(data: notebooks.ICellDto2): vscode.NotebookCellData {
|
||||
return new types.NotebookCellData(
|
||||
NotebookCellKind.to(data.cellKind),
|
||||
data.source,
|
||||
data.language,
|
||||
data.outputs ? data.outputs.map(NotebookCellOutput.to) : undefined,
|
||||
data.metadata ? NotebookCellMetadata.to(data.metadata) : undefined,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookCellOutputItem {
|
||||
@@ -1591,64 +1612,111 @@ export namespace NotebookDecorationRenderOptions {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace NotebookDocumentContentOptions {
|
||||
export function from(options: vscode.NotebookDocumentContentOptions | undefined): notebooks.TransientOptions {
|
||||
return {
|
||||
transientOutputs: options ? options.transientOutputs : false,
|
||||
transientMetadata: {
|
||||
...(options?.transientMetadata ?? {}),
|
||||
...{
|
||||
executionOrder: true,
|
||||
lastRunDuration: true,
|
||||
runState: true,
|
||||
runStartTime: true,
|
||||
lastRunSuccess: true
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestState {
|
||||
export function from(item: vscode.TestState): ITestState {
|
||||
return {
|
||||
state: item.state,
|
||||
duration: item.duration,
|
||||
messages: item.messages?.map(message => ({
|
||||
message: MarkdownString.fromStrict(message.message) || '',
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
actualOutput: message.actualOutput,
|
||||
location: message.location ? location.from(message.location) as any : undefined,
|
||||
})) ?? [],
|
||||
messages: item.messages.map(TestMessage.from),
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestState): vscode.TestState {
|
||||
const ts = new types.TestState(item.state);
|
||||
ts.duration = item.duration;
|
||||
ts.messages = item.messages.map(TestMessage.to);
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestMessage {
|
||||
export function from(message: vscode.TestMessage): ITestMessage {
|
||||
return {
|
||||
state: item.state,
|
||||
messages: item.messages.map(message => ({
|
||||
message: typeof message.message === 'string' ? message.message : MarkdownString.to(message.message),
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
actualOutput: message.actualOutput,
|
||||
location: message.location && location.to({
|
||||
range: message.location.range,
|
||||
uri: URI.revive(message.location.uri)
|
||||
}),
|
||||
})),
|
||||
duration: item.duration,
|
||||
message: MarkdownString.fromStrict(message.message) || '',
|
||||
severity: message.severity,
|
||||
expectedOutput: message.expectedOutput,
|
||||
actualOutput: message.actualOutput,
|
||||
location: message.location ? location.from(message.location) as any : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestMessage): vscode.TestMessage {
|
||||
const message = new types.TestMessage(typeof item.message === 'string' ? item.message : MarkdownString.to(item.message));
|
||||
message.severity = item.severity;
|
||||
message.actualOutput = item.actualOutput;
|
||||
message.expectedOutput = item.expectedOutput;
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestItem {
|
||||
export type Raw = vscode.TestItem;
|
||||
|
||||
export function from(item: vscode.TestItem): ITestItem {
|
||||
return {
|
||||
extId: item.id,
|
||||
label: item.label,
|
||||
location: item.location ? location.from(item.location) as any : undefined,
|
||||
uri: item.uri,
|
||||
range: Range.from(item.range),
|
||||
debuggable: item.debuggable ?? false,
|
||||
description: item.description,
|
||||
runnable: item.runnable ?? true,
|
||||
expandable: item.expandable,
|
||||
};
|
||||
}
|
||||
|
||||
export function toShallow(item: ITestItem): Omit<vscode.RequiredTestItem, 'children'> {
|
||||
export function fromResultSnapshot(item: vscode.TestResultSnapshot): ITestItem {
|
||||
return {
|
||||
extId: item.id,
|
||||
label: item.label,
|
||||
uri: item.uri,
|
||||
range: Range.from(item.range),
|
||||
debuggable: false,
|
||||
description: item.description,
|
||||
runnable: true,
|
||||
expandable: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function toPlain(item: ITestItem): Omit<vscode.TestItem, 'children' | 'invalidate' | 'discoverChildren'> {
|
||||
return {
|
||||
id: item.extId,
|
||||
label: item.label,
|
||||
location: item.location && location.to({
|
||||
range: item.location.range,
|
||||
uri: URI.revive(item.location.uri)
|
||||
}),
|
||||
uri: URI.revive(item.uri),
|
||||
range: Range.to(item.range),
|
||||
expandable: item.expandable,
|
||||
debuggable: item.debuggable,
|
||||
description: item.description,
|
||||
runnable: item.runnable,
|
||||
};
|
||||
}
|
||||
|
||||
export function to(item: ITestItem): types.TestItem {
|
||||
const testItem = new types.TestItem(item.extId, item.label, URI.revive(item.uri), item.expandable);
|
||||
testItem.range = Range.to(item.range);
|
||||
testItem.debuggable = item.debuggable;
|
||||
testItem.description = item.description;
|
||||
testItem.runnable = item.runnable;
|
||||
return testItem;
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TestResults {
|
||||
@@ -1659,7 +1727,7 @@ export namespace TestResults {
|
||||
items: [],
|
||||
};
|
||||
|
||||
const queue: [parent: SerializedTestResultItem | null, children: Iterable<vscode.TestItemWithResults>][] = [
|
||||
const queue: [parent: SerializedTestResultItem | null, children: Iterable<vscode.TestResultSnapshot>][] = [
|
||||
[null, results.results],
|
||||
];
|
||||
|
||||
@@ -1669,11 +1737,12 @@ export namespace TestResults {
|
||||
const serializedItem: SerializedTestResultItem = {
|
||||
children: item.children?.map(c => c.id) ?? [],
|
||||
computedState: item.result.state,
|
||||
item: TestItem.from(item),
|
||||
item: TestItem.fromResultSnapshot(item),
|
||||
state: TestState.from(item.result),
|
||||
retired: undefined,
|
||||
expand: TestItemExpandState.Expanded,
|
||||
parent: parent?.item.extId ?? null,
|
||||
providerId: '',
|
||||
src: { provider: '', tree: -1 },
|
||||
direct: !parent,
|
||||
};
|
||||
|
||||
@@ -1687,8 +1756,8 @@ export namespace TestResults {
|
||||
return serialized;
|
||||
}
|
||||
|
||||
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestItemWithResults => ({
|
||||
...TestItem.toShallow(item.item),
|
||||
const convertTestResultItem = (item: SerializedTestResultItem, byInternalId: Map<string, SerializedTestResultItem>): vscode.TestResultSnapshot => ({
|
||||
...TestItem.toPlain(item.item),
|
||||
result: TestState.to(item.state),
|
||||
children: item.children
|
||||
.map(c => byInternalId.get(c))
|
||||
@@ -1712,3 +1781,16 @@ export namespace TestResults {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export namespace CodeActionTriggerKind {
|
||||
|
||||
export function to(value: modes.CodeActionTriggerType): types.CodeActionTriggerKind {
|
||||
switch (value) {
|
||||
case modes.CodeActionTriggerType.Invoke:
|
||||
return types.CodeActionTriggerKind.Invoke;
|
||||
|
||||
case modes.CodeActionTriggerType.Auto:
|
||||
return types.CodeActionTriggerKind.Automatic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { CellEditType, ICellEditOperation, notebookDocumentMetadataDefaults, NOTEBOOK_DISPLAY_ORDER } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CellEditType, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
function es5ClassCompat(target: Function): any {
|
||||
@@ -673,7 +673,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
// --- notebook
|
||||
|
||||
replaceNotebookMetadata(uri: URI, value: vscode.NotebookDocumentMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.DocumentMetadata, metadata: { ...notebookDocumentMetadataDefaults, ...value } }, notebookMetadata: value });
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.DocumentMetadata, metadata: value }, notebookMetadata: value });
|
||||
}
|
||||
|
||||
replaceNotebookCells(uri: URI, start: number, end: number, cells: vscode.NotebookCellData[], metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
@@ -707,7 +707,7 @@ export class WorkspaceEdit implements vscode.WorkspaceEdit {
|
||||
}
|
||||
|
||||
replaceNotebookCellMetadata(uri: URI, index: number, cellMetadata: vscode.NotebookCellMetadata, metadata?: vscode.WorkspaceEditEntryMetadata): void {
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.Metadata, index, metadata: cellMetadata } });
|
||||
this._edits.push({ _type: FileEditType.Cell, metadata, uri, edit: { editType: CellEditType.PartialMetadata, index, metadata: cellMetadata } });
|
||||
}
|
||||
|
||||
// --- text
|
||||
@@ -1178,9 +1178,9 @@ export class DocumentSymbol {
|
||||
}
|
||||
|
||||
|
||||
export enum CodeActionTrigger {
|
||||
Automatic = 1,
|
||||
Manual = 2,
|
||||
export enum CodeActionTriggerKind {
|
||||
Invoke = 1,
|
||||
Automatic = 2,
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
@@ -2476,9 +2476,11 @@ export class InlineValueEvaluatableExpression implements vscode.InlineValueEvalu
|
||||
@es5ClassCompat
|
||||
export class InlineValueContext implements vscode.InlineValueContext {
|
||||
|
||||
readonly frameId: number;
|
||||
readonly stoppedLocation: vscode.Range;
|
||||
|
||||
constructor(range: vscode.Range) {
|
||||
constructor(frameId: number, range: vscode.Range) {
|
||||
this.frameId = frameId;
|
||||
this.stoppedLocation = range;
|
||||
}
|
||||
}
|
||||
@@ -2906,14 +2908,17 @@ export class NotebookCellRange {
|
||||
return this._end;
|
||||
}
|
||||
|
||||
get isEmpty(): boolean {
|
||||
return this._start === this._end;
|
||||
}
|
||||
|
||||
constructor(start: number, end: number) {
|
||||
// todo@rebornix
|
||||
// if (start < 0) {
|
||||
// throw illegalArgument('start must be positive');
|
||||
// }
|
||||
// if (end < start) {
|
||||
// throw illegalArgument('end cannot be smaller than start');
|
||||
// }
|
||||
if (start < 0) {
|
||||
throw illegalArgument('start must be positive');
|
||||
}
|
||||
if (end < start) {
|
||||
throw illegalArgument('end cannot be smaller than start');
|
||||
}
|
||||
this._start = start;
|
||||
this._end = end;
|
||||
}
|
||||
@@ -2924,13 +2929,8 @@ export class NotebookCellMetadata {
|
||||
constructor(
|
||||
readonly editable?: boolean,
|
||||
readonly breakpointMargin?: boolean,
|
||||
readonly runnable?: boolean,
|
||||
readonly hasExecutionOrder?: boolean,
|
||||
readonly executionOrder?: number,
|
||||
readonly runState?: NotebookCellRunState,
|
||||
readonly runStartTime?: number,
|
||||
readonly statusMessage?: string,
|
||||
readonly lastRunDuration?: number,
|
||||
readonly inputCollapsed?: boolean,
|
||||
readonly outputCollapsed?: boolean,
|
||||
readonly custom?: Record<string, any>,
|
||||
@@ -2939,19 +2939,14 @@ export class NotebookCellMetadata {
|
||||
with(change: {
|
||||
editable?: boolean | null,
|
||||
breakpointMargin?: boolean | null,
|
||||
runnable?: boolean | null,
|
||||
hasExecutionOrder?: boolean | null,
|
||||
executionOrder?: number | null,
|
||||
runState?: NotebookCellRunState | null,
|
||||
runStartTime?: number | null,
|
||||
statusMessage?: string | null,
|
||||
lastRunDuration?: number | null,
|
||||
inputCollapsed?: boolean | null,
|
||||
outputCollapsed?: boolean | null,
|
||||
custom?: Record<string, any> | null,
|
||||
}): NotebookCellMetadata {
|
||||
|
||||
let { editable, breakpointMargin, runnable, hasExecutionOrder, executionOrder, runState, runStartTime, statusMessage, lastRunDuration, inputCollapsed, outputCollapsed, custom } = change;
|
||||
let { editable, breakpointMargin, hasExecutionOrder, statusMessage, inputCollapsed, outputCollapsed, custom } = change;
|
||||
|
||||
if (editable === undefined) {
|
||||
editable = this.editable;
|
||||
@@ -2963,41 +2958,16 @@ export class NotebookCellMetadata {
|
||||
} else if (breakpointMargin === null) {
|
||||
breakpointMargin = undefined;
|
||||
}
|
||||
if (runnable === undefined) {
|
||||
runnable = this.runnable;
|
||||
} else if (runnable === null) {
|
||||
runnable = undefined;
|
||||
}
|
||||
if (hasExecutionOrder === undefined) {
|
||||
hasExecutionOrder = this.hasExecutionOrder;
|
||||
} else if (hasExecutionOrder === null) {
|
||||
hasExecutionOrder = undefined;
|
||||
}
|
||||
if (executionOrder === undefined) {
|
||||
executionOrder = this.executionOrder;
|
||||
} else if (executionOrder === null) {
|
||||
executionOrder = undefined;
|
||||
}
|
||||
if (runState === undefined) {
|
||||
runState = this.runState;
|
||||
} else if (runState === null) {
|
||||
runState = undefined;
|
||||
}
|
||||
if (runStartTime === undefined) {
|
||||
runStartTime = this.runStartTime;
|
||||
} else if (runStartTime === null) {
|
||||
runStartTime = undefined;
|
||||
}
|
||||
if (statusMessage === undefined) {
|
||||
statusMessage = this.statusMessage;
|
||||
} else if (statusMessage === null) {
|
||||
statusMessage = undefined;
|
||||
}
|
||||
if (lastRunDuration === undefined) {
|
||||
lastRunDuration = this.lastRunDuration;
|
||||
} else if (lastRunDuration === null) {
|
||||
lastRunDuration = undefined;
|
||||
}
|
||||
if (inputCollapsed === undefined) {
|
||||
inputCollapsed = this.inputCollapsed;
|
||||
} else if (inputCollapsed === null) {
|
||||
@@ -3016,13 +2986,8 @@ export class NotebookCellMetadata {
|
||||
|
||||
if (editable === this.editable &&
|
||||
breakpointMargin === this.breakpointMargin &&
|
||||
runnable === this.runnable &&
|
||||
hasExecutionOrder === this.hasExecutionOrder &&
|
||||
executionOrder === this.executionOrder &&
|
||||
runState === this.runState &&
|
||||
runStartTime === this.runStartTime &&
|
||||
statusMessage === this.statusMessage &&
|
||||
lastRunDuration === this.lastRunDuration &&
|
||||
inputCollapsed === this.inputCollapsed &&
|
||||
outputCollapsed === this.outputCollapsed &&
|
||||
custom === this.custom
|
||||
@@ -3033,13 +2998,8 @@ export class NotebookCellMetadata {
|
||||
return new NotebookCellMetadata(
|
||||
editable,
|
||||
breakpointMargin,
|
||||
runnable,
|
||||
hasExecutionOrder,
|
||||
executionOrder,
|
||||
runState,
|
||||
runStartTime,
|
||||
statusMessage,
|
||||
lastRunDuration,
|
||||
inputCollapsed,
|
||||
outputCollapsed,
|
||||
custom,
|
||||
@@ -3051,70 +3011,42 @@ export class NotebookDocumentMetadata {
|
||||
|
||||
constructor(
|
||||
readonly editable: boolean = true,
|
||||
readonly runnable: boolean = true,
|
||||
readonly cellEditable: boolean = true,
|
||||
readonly cellRunnable: boolean = true,
|
||||
readonly cellHasExecutionOrder: boolean = true,
|
||||
readonly displayOrder: vscode.GlobPattern[] = NOTEBOOK_DISPLAY_ORDER,
|
||||
readonly custom: { [key: string]: any; } = {},
|
||||
readonly runState: NotebookRunState = NotebookRunState.Idle,
|
||||
readonly trusted: boolean = true,
|
||||
) { }
|
||||
|
||||
with(change: {
|
||||
editable?: boolean | null,
|
||||
runnable?: boolean | null,
|
||||
cellEditable?: boolean | null,
|
||||
cellRunnable?: boolean | null,
|
||||
cellHasExecutionOrder?: boolean | null,
|
||||
displayOrder?: vscode.GlobPattern[] | null,
|
||||
custom?: { [key: string]: any; } | null,
|
||||
runState?: NotebookRunState | null,
|
||||
trusted?: boolean | null,
|
||||
}): NotebookDocumentMetadata {
|
||||
|
||||
let { editable, runnable, cellEditable, cellRunnable, cellHasExecutionOrder, displayOrder, custom, runState, trusted } = change;
|
||||
let { editable, cellEditable, cellHasExecutionOrder, custom, trusted } = change;
|
||||
|
||||
if (editable === undefined) {
|
||||
editable = this.editable;
|
||||
} else if (editable === null) {
|
||||
editable = undefined;
|
||||
}
|
||||
if (runnable === undefined) {
|
||||
runnable = this.runnable;
|
||||
} else if (runnable === null) {
|
||||
runnable = undefined;
|
||||
}
|
||||
if (cellEditable === undefined) {
|
||||
cellEditable = this.cellEditable;
|
||||
} else if (cellEditable === null) {
|
||||
cellEditable = undefined;
|
||||
}
|
||||
if (cellRunnable === undefined) {
|
||||
cellRunnable = this.cellRunnable;
|
||||
} else if (cellRunnable === null) {
|
||||
cellRunnable = undefined;
|
||||
}
|
||||
if (cellHasExecutionOrder === undefined) {
|
||||
cellHasExecutionOrder = this.cellHasExecutionOrder;
|
||||
} else if (cellHasExecutionOrder === null) {
|
||||
cellHasExecutionOrder = undefined;
|
||||
}
|
||||
if (displayOrder === undefined) {
|
||||
displayOrder = this.displayOrder;
|
||||
} else if (displayOrder === null) {
|
||||
displayOrder = undefined;
|
||||
}
|
||||
if (custom === undefined) {
|
||||
custom = this.custom;
|
||||
} else if (custom === null) {
|
||||
custom = undefined;
|
||||
}
|
||||
if (runState === undefined) {
|
||||
runState = this.runState;
|
||||
} else if (runState === null) {
|
||||
runState = undefined;
|
||||
}
|
||||
if (trusted === undefined) {
|
||||
trusted = this.trusted;
|
||||
} else if (trusted === null) {
|
||||
@@ -3122,13 +3054,9 @@ export class NotebookDocumentMetadata {
|
||||
}
|
||||
|
||||
if (editable === this.editable &&
|
||||
runnable === this.runnable &&
|
||||
cellEditable === this.cellEditable &&
|
||||
cellRunnable === this.cellRunnable &&
|
||||
cellHasExecutionOrder === this.cellHasExecutionOrder &&
|
||||
displayOrder === this.displayOrder &&
|
||||
custom === this.custom &&
|
||||
runState === this.runState &&
|
||||
trusted === this.trusted
|
||||
) {
|
||||
return this;
|
||||
@@ -3137,20 +3065,45 @@ export class NotebookDocumentMetadata {
|
||||
|
||||
return new NotebookDocumentMetadata(
|
||||
editable,
|
||||
runnable,
|
||||
cellEditable,
|
||||
cellRunnable,
|
||||
cellHasExecutionOrder,
|
||||
displayOrder,
|
||||
custom,
|
||||
runState,
|
||||
trusted
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookCellData {
|
||||
|
||||
kind: NotebookCellKind;
|
||||
source: string;
|
||||
language: string;
|
||||
outputs?: NotebookCellOutput[];
|
||||
metadata?: NotebookCellMetadata;
|
||||
latestExecutionSummary?: vscode.NotebookCellExecutionSummary;
|
||||
|
||||
constructor(kind: NotebookCellKind, source: string, language: string, outputs?: NotebookCellOutput[], metadata?: NotebookCellMetadata, latestExecutionSummary?: vscode.NotebookCellExecutionSummary) {
|
||||
this.kind = kind;
|
||||
this.source = source;
|
||||
this.language = language;
|
||||
this.outputs = outputs ?? [];
|
||||
this.metadata = metadata;
|
||||
this.latestExecutionSummary = latestExecutionSummary;
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookData {
|
||||
|
||||
cells: NotebookCellData[];
|
||||
metadata: NotebookDocumentMetadata;
|
||||
|
||||
constructor(cells: NotebookCellData[], metadata?: NotebookDocumentMetadata) {
|
||||
this.cells = cells;
|
||||
this.metadata = metadata ?? new NotebookDocumentMetadata();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class NotebookCellOutputItem {
|
||||
|
||||
static isNotebookCellOutputItem(obj: unknown): obj is vscode.NotebookCellOutputItem {
|
||||
@@ -3191,16 +3144,10 @@ export enum NotebookCellKind {
|
||||
Code = 2
|
||||
}
|
||||
|
||||
export enum NotebookCellRunState {
|
||||
Running = 1,
|
||||
Idle = 2,
|
||||
Success = 3,
|
||||
Error = 4
|
||||
}
|
||||
|
||||
export enum NotebookRunState {
|
||||
Running = 1,
|
||||
Idle = 2
|
||||
export enum NotebookCellExecutionState {
|
||||
Idle = 1,
|
||||
Pending = 2,
|
||||
Executing = 3,
|
||||
}
|
||||
|
||||
export enum NotebookCellStatusBarAlignment {
|
||||
@@ -3276,7 +3223,7 @@ export class LinkedEditingRanges {
|
||||
}
|
||||
|
||||
//#region Testing
|
||||
export enum TestRunState {
|
||||
export enum TestResult {
|
||||
Unset = 0,
|
||||
Queued = 1,
|
||||
Running = 2,
|
||||
@@ -3293,9 +3240,155 @@ export enum TestMessageSeverity {
|
||||
Hint = 3
|
||||
}
|
||||
|
||||
export type RequiredTestItem = vscode.RequiredTestItem;
|
||||
export const TestItemHookProperty = Symbol('TestItemHookProperty');
|
||||
|
||||
export type TestItem = vscode.TestItem;
|
||||
export interface ITestItemHook {
|
||||
created(item: vscode.TestItem): void;
|
||||
setProp<K extends keyof vscode.TestItem>(key: K, value: vscode.TestItem[K]): void;
|
||||
invalidate(id: string): void;
|
||||
delete(id: string): void;
|
||||
}
|
||||
|
||||
const testItemPropAccessor = <K extends keyof vscode.TestItem>(item: TestItem, key: K, defaultValue: vscode.TestItem[K]) => {
|
||||
let value = defaultValue;
|
||||
return {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get() {
|
||||
return value;
|
||||
},
|
||||
set(newValue: vscode.TestItem[K]) {
|
||||
item[TestItemHookProperty]?.setProp(key, newValue);
|
||||
value = newValue;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export class TestChildrenCollection implements vscode.TestChildrenCollection<vscode.TestItem> {
|
||||
#map = new Map<string, vscode.TestItem>();
|
||||
#hookRef: () => ITestItemHook | undefined;
|
||||
|
||||
public get size() {
|
||||
return this.#map.size;
|
||||
}
|
||||
|
||||
constructor(hookRef: () => ITestItemHook | undefined) {
|
||||
this.#hookRef = hookRef;
|
||||
}
|
||||
|
||||
public add(child: vscode.TestItem) {
|
||||
const map = this.#map;
|
||||
const hook = this.#hookRef();
|
||||
|
||||
const existing = map.get(child.id);
|
||||
if (existing === child) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (existing) {
|
||||
hook?.delete(child.id);
|
||||
}
|
||||
|
||||
map.set(child.id, child);
|
||||
hook?.created(child);
|
||||
}
|
||||
|
||||
public get(id: string) {
|
||||
return this.#map.get(id);
|
||||
}
|
||||
|
||||
public clear() {
|
||||
for (const key of this.#map.keys()) {
|
||||
this.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
public delete(childOrId: vscode.TestItem | string) {
|
||||
const id = typeof childOrId === 'string' ? childOrId : childOrId.id;
|
||||
if (this.#map.has(id)) {
|
||||
this.#map.delete(id);
|
||||
this.#hookRef()?.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
public toJSON() {
|
||||
return [...this.#map.values()];
|
||||
}
|
||||
|
||||
public [Symbol.iterator]() {
|
||||
return this.#map.values();
|
||||
}
|
||||
}
|
||||
|
||||
export class TestItem implements vscode.TestItem {
|
||||
public id!: string;
|
||||
public range!: vscode.Range | undefined;
|
||||
public description!: string | undefined;
|
||||
public runnable!: boolean;
|
||||
public debuggable!: boolean;
|
||||
public children!: TestChildrenCollection;
|
||||
public uri!: vscode.Uri;
|
||||
public [TestItemHookProperty]!: ITestItemHook | undefined;
|
||||
|
||||
constructor(id: string, public label: string, uri: vscode.Uri, public expandable: boolean) {
|
||||
Object.defineProperties(this, {
|
||||
id: {
|
||||
value: id,
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
},
|
||||
uri: {
|
||||
value: uri,
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
},
|
||||
children: {
|
||||
value: new TestChildrenCollection(() => this[TestItemHookProperty]),
|
||||
enumerable: true,
|
||||
writable: false,
|
||||
},
|
||||
[TestItemHookProperty]: {
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: false,
|
||||
},
|
||||
range: testItemPropAccessor(this, 'range', undefined),
|
||||
description: testItemPropAccessor(this, 'description', undefined),
|
||||
runnable: testItemPropAccessor(this, 'runnable', true),
|
||||
debuggable: testItemPropAccessor(this, 'debuggable', true),
|
||||
});
|
||||
}
|
||||
|
||||
public invalidate() {
|
||||
this[TestItemHookProperty]?.invalidate(this.id);
|
||||
}
|
||||
|
||||
public discoverChildren(progress: vscode.Progress<{ busy: boolean }>, _token: vscode.CancellationToken) {
|
||||
progress.report({ busy: false });
|
||||
}
|
||||
}
|
||||
|
||||
export class TestState implements vscode.TestState {
|
||||
public messages: TestMessage[] = [];
|
||||
public duration?: number;
|
||||
|
||||
constructor(public state: TestResult) { }
|
||||
}
|
||||
|
||||
export class TestMessage implements vscode.TestMessage {
|
||||
public severity = TestMessageSeverity.Error;
|
||||
public expectedOutput?: string;
|
||||
public actualOutput?: string;
|
||||
|
||||
public static diff(message: string | vscode.MarkdownString, expected: string, actual: string) {
|
||||
const msg = new TestMessage(message);
|
||||
msg.expectedOutput = expected;
|
||||
msg.actualOutput = actual;
|
||||
return msg;
|
||||
}
|
||||
|
||||
constructor(public message: string | vscode.MarkdownString) { }
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
@@ -3311,3 +3404,11 @@ export enum WorkspaceTrustState {
|
||||
Trusted = 1,
|
||||
Unknown = 2
|
||||
}
|
||||
|
||||
export enum PortAutoForwardAction {
|
||||
Notify = 1,
|
||||
OpenBrowser = 2,
|
||||
OpenPreview = 3,
|
||||
Silent = 4,
|
||||
Ignore = 5
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
@@ -97,7 +96,7 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
|
||||
public set options(newOptions: vscode.WebviewOptions) {
|
||||
this.assertNotDisposed();
|
||||
this.#proxy.$setOptions(this.#handle, convertWebviewOptions(this.#extension, this.#workspace, newOptions));
|
||||
this.#proxy.$setOptions(this.#handle, serializeWebviewOptions(this.#extension, this.#workspace, newOptions));
|
||||
this.#options = newOptions;
|
||||
}
|
||||
|
||||
@@ -148,7 +147,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
||||
this._logService.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`);
|
||||
}
|
||||
|
||||
public createNewWebview(handle: string, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, extension: IExtensionDescription): ExtHostWebview {
|
||||
public createNewWebview(handle: string, options: extHostProtocol.IWebviewOptions, extension: IExtensionDescription): ExtHostWebview {
|
||||
const webview = new ExtHostWebview(handle, this._webviewProxy, reviveOptions(options), this.initData, this.workspace, extension, this._deprecationService);
|
||||
this._webviews.set(handle, webview);
|
||||
|
||||
@@ -170,22 +169,24 @@ export function toExtensionData(extension: IExtensionDescription): extHostProtoc
|
||||
return { id: extension.identifier, location: extension.extensionLocation };
|
||||
}
|
||||
|
||||
export function convertWebviewOptions(
|
||||
export function serializeWebviewOptions(
|
||||
extension: IExtensionDescription,
|
||||
workspace: IExtHostWorkspace | undefined,
|
||||
options: vscode.WebviewPanelOptions & vscode.WebviewOptions,
|
||||
): modes.IWebviewOptions {
|
||||
options: vscode.WebviewOptions,
|
||||
): extHostProtocol.IWebviewOptions {
|
||||
return {
|
||||
...options,
|
||||
enableCommandUris: options.enableCommandUris,
|
||||
enableScripts: options.enableScripts,
|
||||
portMapping: options.portMapping,
|
||||
localResourceRoots: options.localResourceRoots || getDefaultLocalResourceRoots(extension, workspace)
|
||||
};
|
||||
}
|
||||
|
||||
function reviveOptions(
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions
|
||||
): vscode.WebviewOptions {
|
||||
export function reviveOptions(options: extHostProtocol.IWebviewOptions): vscode.WebviewOptions {
|
||||
return {
|
||||
...options,
|
||||
enableCommandUris: options.enableCommandUris,
|
||||
enableScripts: options.enableScripts,
|
||||
portMapping: options.portMapping,
|
||||
localResourceRoots: options.localResourceRoots?.map(components => URI.from(components)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,10 +7,9 @@ import { Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { convertWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { serializeWebviewOptions, ExtHostWebview, ExtHostWebviews, toExtensionData } from 'vs/workbench/api/common/extHostWebview';
|
||||
import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { EditorGroupColumn } from 'vs/workbench/common/editor';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -48,14 +47,14 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel {
|
||||
viewType: string,
|
||||
title: string,
|
||||
viewColumn: vscode.ViewColumn | undefined,
|
||||
editorOptions: vscode.WebviewPanelOptions,
|
||||
panelOptions: vscode.WebviewPanelOptions,
|
||||
webview: ExtHostWebview
|
||||
) {
|
||||
super();
|
||||
this.#handle = handle;
|
||||
this.#proxy = proxy;
|
||||
this.#viewType = viewType;
|
||||
this.#options = editorOptions;
|
||||
this.#options = panelOptions;
|
||||
this.#viewColumn = viewColumn;
|
||||
this.#title = title;
|
||||
this.#webview = webview;
|
||||
@@ -201,7 +200,11 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
};
|
||||
|
||||
const handle = ExtHostWebviewPanels.newHandle();
|
||||
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, title, webviewShowOptions, convertWebviewOptions(extension, this.workspace, options));
|
||||
this._proxy.$createWebviewPanel(toExtensionData(extension), handle, viewType, {
|
||||
title,
|
||||
panelOptions: serializeWebviewPanelOptions(options),
|
||||
webviewOptions: serializeWebviewOptions(extension, this.workspace, options),
|
||||
}, webviewShowOptions);
|
||||
|
||||
const webview = this.webviews.createNewWebview(handle, options, extension);
|
||||
const panel = this.createNewWebviewPanel(handle, viewType, title, viewColumn, options, webview);
|
||||
@@ -271,10 +274,13 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
async $deserializeWebviewPanel(
|
||||
webviewHandle: extHostProtocol.WebviewHandle,
|
||||
viewType: string,
|
||||
title: string,
|
||||
state: any,
|
||||
position: EditorGroupColumn,
|
||||
options: modes.IWebviewOptions & modes.IWebviewPanelOptions
|
||||
initData: {
|
||||
title: string;
|
||||
state: any;
|
||||
webviewOptions: extHostProtocol.IWebviewOptions;
|
||||
panelOptions: extHostProtocol.IWebviewPanelOptions;
|
||||
},
|
||||
position: EditorGroupColumn
|
||||
): Promise<void> {
|
||||
const entry = this._serializers.get(viewType);
|
||||
if (!entry) {
|
||||
@@ -282,12 +288,12 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
}
|
||||
const { serializer, extension } = entry;
|
||||
|
||||
const webview = this.webviews.createNewWebview(webviewHandle, options, extension);
|
||||
const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, title, position, options, webview);
|
||||
await serializer.deserializeWebviewPanel(revivedPanel, state);
|
||||
const webview = this.webviews.createNewWebview(webviewHandle, initData.webviewOptions, extension);
|
||||
const revivedPanel = this.createNewWebviewPanel(webviewHandle, viewType, initData.title, position, initData.panelOptions, webview);
|
||||
await serializer.deserializeWebviewPanel(revivedPanel, initData.state);
|
||||
}
|
||||
|
||||
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: vscode.ViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions, webview: ExtHostWebview) {
|
||||
public createNewWebviewPanel(webviewHandle: string, viewType: string, title: string, position: vscode.ViewColumn, options: extHostProtocol.IWebviewPanelOptions, webview: ExtHostWebview) {
|
||||
const panel = new ExtHostWebviewPanel(webviewHandle, this._proxy, viewType, title, position, options, webview);
|
||||
this._webviewPanels.set(webviewHandle, panel);
|
||||
return panel;
|
||||
@@ -297,3 +303,10 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
}
|
||||
|
||||
function serializeWebviewPanelOptions(options: vscode.WebviewPanelOptions): extHostProtocol.IWebviewPanelOptions {
|
||||
return {
|
||||
enableFindWidget: options.enableFindWidget,
|
||||
retainContextWhenHidden: options.retainContextWhenHidden,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -563,8 +563,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
return this._workspaceTrustState;
|
||||
}
|
||||
|
||||
requireWorkspaceTrust(modal?: boolean): Promise<WorkspaceTrustState> {
|
||||
return this._proxy.$requireWorkspaceTrust(modal);
|
||||
requireWorkspaceTrust(options?: vscode.WorkspaceTrustRequestOptions): Promise<WorkspaceTrustState> {
|
||||
return this._proxy.$requireWorkspaceTrust(options);
|
||||
}
|
||||
|
||||
$onDidChangeWorkspaceTrustState(state: WorkspaceTrustStateChangeEvent): void {
|
||||
|
||||
@@ -53,6 +53,11 @@ const apiMenus: IAPIMenu[] = [
|
||||
id: MenuId.EditorContext,
|
||||
description: localize('menus.editorContext', "The editor context menu")
|
||||
},
|
||||
{
|
||||
key: 'editor/context/copy',
|
||||
id: MenuId.EditorContextCopy,
|
||||
description: localize('menus.editorContextCopyAs', "'Copy as' submenu in the editor context menu")
|
||||
},
|
||||
{
|
||||
key: 'explorer/context',
|
||||
id: MenuId.ExplorerContext,
|
||||
@@ -91,6 +96,11 @@ const apiMenus: IAPIMenu[] = [
|
||||
proposed: true,
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'menuBar/edit/copy',
|
||||
id: MenuId.MenubarCopy,
|
||||
description: localize('menus.opy', "'Copy as' submenu in the top level Edit menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/title',
|
||||
id: MenuId.SCMTitle,
|
||||
@@ -104,17 +114,17 @@ const apiMenus: IAPIMenu[] = [
|
||||
{
|
||||
key: 'scm/resourceState/context',
|
||||
id: MenuId.SCMResourceContext,
|
||||
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu")
|
||||
description: localize('menus.resourceStateContext', "The Source Control resource state context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/resourceFolder/context',
|
||||
id: MenuId.SCMResourceFolderContext,
|
||||
description: localize('menus.resourceStateContext', "The Source Control resource state context menu")
|
||||
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/resourceGroup/context',
|
||||
id: MenuId.SCMResourceGroupContext,
|
||||
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu")
|
||||
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/change/title',
|
||||
@@ -160,6 +170,12 @@ const apiMenus: IAPIMenu[] = [
|
||||
description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'notebook/toolbar',
|
||||
id: MenuId.NotebookToolbar,
|
||||
description: localize('notebook.toolbar', "The contributed notebook toolbar menu"),
|
||||
proposed: true
|
||||
},
|
||||
{
|
||||
key: 'notebook/cell/title',
|
||||
id: MenuId.NotebookCellTitle,
|
||||
@@ -539,7 +555,7 @@ commandsExtensionPoint.setHandler(extensions => {
|
||||
let absoluteIcon: { dark: URI; light?: URI; } | ThemeIcon | undefined;
|
||||
if (icon) {
|
||||
if (typeof icon === 'string') {
|
||||
absoluteIcon = ThemeIcon.fromString(icon) || { dark: resources.joinPath(extension.description.extensionLocation, icon) };
|
||||
absoluteIcon = ThemeIcon.fromString(icon) ?? { dark: resources.joinPath(extension.description.extensionLocation, icon), light: resources.joinPath(extension.description.extensionLocation, icon) };
|
||||
|
||||
} else {
|
||||
absoluteIcon = {
|
||||
|
||||
@@ -173,7 +173,6 @@ export class CLIServerBase {
|
||||
}
|
||||
|
||||
private async manageExtensions(data: ExtensionManagementPipeArgs, res: http.ServerResponse) {
|
||||
console.log('server: manageExtensions');
|
||||
try {
|
||||
const toExtOrVSIX = (inputs: string[] | undefined) => inputs?.map(input => /\.vsix$/i.test(input) ? URI.parse(input) : input);
|
||||
const commandArgs = {
|
||||
@@ -182,12 +181,16 @@ export class CLIServerBase {
|
||||
uninstall: toExtOrVSIX(data.uninstall),
|
||||
force: data.force
|
||||
};
|
||||
const output = await this._commands.executeCommand('_remoteCLI.manageExtensions', commandArgs, { allowTunneling: true });
|
||||
const output = await this._commands.executeCommand('_remoteCLI.manageExtensions', commandArgs);
|
||||
res.writeHead(200);
|
||||
res.write(output);
|
||||
} catch (e) {
|
||||
} catch (err) {
|
||||
res.writeHead(500);
|
||||
res.write(String(e));
|
||||
res.write(String(err), err => {
|
||||
if (err) {
|
||||
this.logService.error(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
res.end();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import type * as vscode from 'vscode';
|
||||
import * as env from 'vs/base/common/platform';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { DebugAdapterExecutable } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { ExecutableDebugAdapter, SocketDebugAdapter, NamedPipeDebugAdapter } from 'vs/workbench/contrib/debug/node/debugAdapter';
|
||||
import { AbstractDebugAdapter } from 'vs/workbench/contrib/debug/common/abstractDebugAdapter';
|
||||
@@ -110,10 +110,23 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
if (giveShellTimeToInitialize) {
|
||||
// give a new terminal some time to initialize the shell
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
} else {
|
||||
if (configProvider.getConfiguration('debug.terminal').get<boolean>('clearBeforeReusing')) {
|
||||
// clear terminal before reusing it
|
||||
if (shell.indexOf('powershell') >= 0 || shell.indexOf('pwsh') >= 0 || shell.indexOf('cmd.exe') >= 0) {
|
||||
terminal.sendText('cls');
|
||||
} else if (shell.indexOf('bash') >= 0) {
|
||||
terminal.sendText('clear');
|
||||
} else if (platform.isWindows) {
|
||||
terminal.sendText('cls');
|
||||
} else {
|
||||
terminal.sendText('clear');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env);
|
||||
terminal.sendText(command, true);
|
||||
terminal.sendText(command);
|
||||
|
||||
// Mark terminal as unused when its session ends, see #112055
|
||||
const sessionListener = this.onDidTerminateDebugSession(s => {
|
||||
@@ -133,7 +146,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
|
||||
}
|
||||
|
||||
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
|
||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment, this._workspaceService);
|
||||
return new ExtHostVariableResolverService(folders, editorService, configurationService, this._workspaceService);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Schemas } from 'vs/base/common/network';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
|
||||
import { realpathSync } from 'vs/base/node/extpath';
|
||||
|
||||
class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
|
||||
@@ -36,7 +37,7 @@ class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
}
|
||||
return that._factories.get(request)!.load(
|
||||
request,
|
||||
URI.file(parent.filename),
|
||||
URI.file(realpathSync(parent.filename)),
|
||||
request => original.apply(this, [request, parent, isMain])
|
||||
);
|
||||
};
|
||||
|
||||
@@ -35,13 +35,15 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
) {
|
||||
super(extHostRpc, _uriTransformer, _logService);
|
||||
|
||||
const outputChannel = new OutputChannel('RipgrepSearchUD', this._logService);
|
||||
this.registerTextSearchProvider(Schemas.userData, new RipgrepSearchProvider(outputChannel));
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this._registerEHSearchProviders();
|
||||
}
|
||||
}
|
||||
|
||||
private _registerEHSearchProviders(): void {
|
||||
const outputChannel = new OutputChannel(this._logService);
|
||||
const outputChannel = new OutputChannel('RipgrepSearchEH', this._logService);
|
||||
this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel));
|
||||
this.registerInternalFileSearchProvider(Schemas.file, new SearchService());
|
||||
}
|
||||
@@ -101,4 +103,3 @@ export class NativeExtHostSearch extends ExtHostSearch {
|
||||
return new NativeTextSearchManager(query, provider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData
|
||||
import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecutionDTO, HandlerData } from 'vs/workbench/api/common/extHostTask';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService';
|
||||
|
||||
export class ExtHostTask extends ExtHostTaskBase {
|
||||
@@ -122,7 +121,7 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
private async getVariableResolver(workspaceFolders: vscode.WorkspaceFolder[]): Promise<ExtHostVariableResolverService> {
|
||||
if (this._variableResolver === undefined) {
|
||||
const configProvider = await this._configurationService.getConfigProvider();
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, process.env as IProcessEnvironment, this.workspaceService);
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, this.workspaceService);
|
||||
}
|
||||
return this._variableResolver;
|
||||
}
|
||||
|
||||
@@ -3,33 +3,28 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type * as vscode from 'vscode';
|
||||
import * as os from 'os';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfiguration, ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { TerminalProcess } from 'vs/platform/terminal/node/terminalProcess';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfigProvider, ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal';
|
||||
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
|
||||
import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { getSystemShell, getSystemShellSync } from 'vs/base/node/shell';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError } from 'vs/platform/terminal/common/terminal';
|
||||
import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { ITerminalConfiguration, ITerminalProfile } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
|
||||
import { detectAvailableProfiles } from 'vs/workbench/contrib/terminal/node/terminalProfiles';
|
||||
import type * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
private _variableResolver: ExtHostVariableResolverService | undefined;
|
||||
private _variableResolverPromise: Promise<ExtHostVariableResolverService>;
|
||||
private _lastActiveWorkspace: IWorkspaceFolder | undefined;
|
||||
|
||||
// TODO: Pull this from main side
|
||||
@@ -41,18 +36,17 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
@IExtHostConfiguration private _extHostConfiguration: ExtHostConfiguration,
|
||||
@IExtHostWorkspace private _extHostWorkspace: ExtHostWorkspace,
|
||||
@IExtHostDocumentsAndEditors private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
@ILogService private _logService: ILogService,
|
||||
@IExtHostInitDataService private _extHostInitDataService: IExtHostInitDataService
|
||||
@ILogService private _logService: ILogService
|
||||
) {
|
||||
super(true, extHostRpc);
|
||||
|
||||
// Getting the SystemShell is an async operation, however, the ExtHost terminal service is mostly synchronous
|
||||
// and the API `vscode.env.shell` is also synchronous. The default shell _should_ be set when extensions are
|
||||
// starting up but if not, we run getSystemShellSync below which gets a sane default.
|
||||
getSystemShell(platform.platform).then(s => this._defaultShell = s);
|
||||
getSystemShell(platform.platform, process.env as platform.IProcessEnvironment).then(s => this._defaultShell = s);
|
||||
|
||||
this._updateLastActiveWorkspace();
|
||||
this._updateVariableResolver();
|
||||
this._variableResolverPromise = this._updateVariableResolver();
|
||||
this._registerListeners();
|
||||
}
|
||||
|
||||
@@ -91,7 +85,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
return terminalEnvironment.getDefaultShell(
|
||||
fetchSetting,
|
||||
this._isWorkspaceShellAllowed,
|
||||
this._defaultShell ?? getSystemShellSync(platform.platform),
|
||||
this._defaultShell ?? getSystemShellSync(platform.platform, process.env as platform.IProcessEnvironment),
|
||||
process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'),
|
||||
process.env.windir,
|
||||
terminalEnvironment.createVariableResolver(this._lastActiveWorkspace, this._variableResolver),
|
||||
@@ -121,15 +115,11 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
};
|
||||
}
|
||||
|
||||
private async _getNonInheritedEnv(): Promise<platform.IProcessEnvironment> {
|
||||
const env = await getMainProcessParentEnv();
|
||||
env.VSCODE_IPC_HOOK_CLI = process.env['VSCODE_IPC_HOOK_CLI']!;
|
||||
return env;
|
||||
}
|
||||
|
||||
private _registerListeners(): void {
|
||||
this._extHostDocumentsAndEditors.onDidChangeActiveTextEditor(() => this._updateLastActiveWorkspace());
|
||||
this._extHostWorkspace.onDidChangeWorkspace(() => this._updateVariableResolver());
|
||||
this._extHostWorkspace.onDidChangeWorkspace(() => {
|
||||
this._variableResolverPromise = this._updateVariableResolver();
|
||||
});
|
||||
}
|
||||
|
||||
private _updateLastActiveWorkspace(): void {
|
||||
@@ -139,106 +129,16 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
}
|
||||
}
|
||||
|
||||
private async _updateVariableResolver(): Promise<void> {
|
||||
private async _updateVariableResolver(): Promise<ExtHostVariableResolverService> {
|
||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2();
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider, process.env as platform.IProcessEnvironment);
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider);
|
||||
return this._variableResolver;
|
||||
}
|
||||
|
||||
public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined> {
|
||||
const shellLaunchConfig: IShellLaunchConfig = {
|
||||
name: shellLaunchConfigDto.name,
|
||||
executable: shellLaunchConfigDto.executable,
|
||||
args: shellLaunchConfigDto.args,
|
||||
cwd: typeof shellLaunchConfigDto.cwd === 'string' ? shellLaunchConfigDto.cwd : URI.revive(shellLaunchConfigDto.cwd),
|
||||
env: shellLaunchConfigDto.env,
|
||||
flowControl: shellLaunchConfigDto.flowControl
|
||||
};
|
||||
|
||||
// Merge in shell and args from settings
|
||||
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
if (!shellLaunchConfig.executable) {
|
||||
shellLaunchConfig.executable = this.getDefaultShell(false, configProvider);
|
||||
shellLaunchConfig.args = this.getDefaultShellArgs(false, configProvider);
|
||||
} else {
|
||||
if (this._variableResolver) {
|
||||
shellLaunchConfig.executable = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.executable);
|
||||
if (shellLaunchConfig.args) {
|
||||
if (Array.isArray(shellLaunchConfig.args)) {
|
||||
const resolvedArgs: string[] = [];
|
||||
for (const arg of shellLaunchConfig.args) {
|
||||
resolvedArgs.push(this._variableResolver.resolve(this._lastActiveWorkspace, arg));
|
||||
}
|
||||
shellLaunchConfig.args = resolvedArgs;
|
||||
} else {
|
||||
shellLaunchConfig.args = this._variableResolver.resolve(this._lastActiveWorkspace, shellLaunchConfig.args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents);
|
||||
let lastActiveWorkspace: IWorkspaceFolder | undefined;
|
||||
if (activeWorkspaceRootUriComponents && activeWorkspaceRootUri) {
|
||||
// Get the environment
|
||||
const apiLastActiveWorkspace = await this._extHostWorkspace.getWorkspaceFolder(activeWorkspaceRootUri);
|
||||
if (apiLastActiveWorkspace) {
|
||||
lastActiveWorkspace = {
|
||||
uri: apiLastActiveWorkspace.uri,
|
||||
name: apiLastActiveWorkspace.name,
|
||||
index: apiLastActiveWorkspace.index,
|
||||
toResource: () => {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get the initial cwd
|
||||
const terminalConfig = configProvider.getConfiguration('terminal.integrated');
|
||||
|
||||
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._variableResolver), activeWorkspaceRootUri, terminalConfig.cwd, this._logService);
|
||||
shellLaunchConfig.cwd = initialCwd;
|
||||
|
||||
const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect<ITerminalEnvironment>(`env.${platformKey}`));
|
||||
const baseEnv = terminalConfig.get<boolean>('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await this._getNonInheritedEnv();
|
||||
const variableResolver = terminalEnvironment.createVariableResolver(lastActiveWorkspace, this._variableResolver);
|
||||
const env = terminalEnvironment.createTerminalEnvironment(
|
||||
shellLaunchConfig,
|
||||
envFromConfig,
|
||||
variableResolver,
|
||||
isWorkspaceShellAllowed,
|
||||
this._extHostInitDataService.version,
|
||||
terminalConfig.get<'auto' | 'off' | 'on'>('detectLocale', 'auto'),
|
||||
baseEnv
|
||||
);
|
||||
|
||||
// Apply extension environment variable collections to the environment
|
||||
if (!shellLaunchConfig.strictEnv && !shellLaunchConfig.hideFromUser) {
|
||||
const mergedCollection = new MergedEnvironmentVariableCollection(this._environmentVariableCollections);
|
||||
mergedCollection.applyToProcessEnvironment(env, variableResolver);
|
||||
}
|
||||
|
||||
this._proxy.$sendResolvedLaunchConfig(id, shellLaunchConfig);
|
||||
// Fork the process and listen for messages
|
||||
this._logService.debug(`Terminal process launching on ext host`, { shellLaunchConfig, initialCwd, cols, rows, env });
|
||||
// TODO: Support conpty on remote, it doesn't seem to work for some reason?
|
||||
// TODO: When conpty is enabled, only enable it when accessibilityMode is off
|
||||
const enableConpty = false; //terminalConfig.get('windowsEnableConpty') as boolean;
|
||||
|
||||
const terminalProcess = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, process.env as platform.IProcessEnvironment, enableConpty, this._logService);
|
||||
this._setupExtHostProcessListeners(id, terminalProcess);
|
||||
const error = await terminalProcess.start();
|
||||
if (error) {
|
||||
// TODO: Teardown?
|
||||
return error;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public $getAvailableShells(): Promise<IShellDefinitionDto[]> {
|
||||
return detectAvailableShells();
|
||||
public async $getAvailableProfiles(quickLaunchOnly: boolean): Promise<ITerminalProfile[]> {
|
||||
const config = await (await this._extHostConfiguration.getConfigProvider()).getConfiguration().get('terminal.integrated');
|
||||
return detectAvailableProfiles(quickLaunchOnly, this._logService, config as ITerminalConfiguration, await this._variableResolverPromise, this._lastActiveWorkspace);
|
||||
}
|
||||
|
||||
public async $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto> {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MainThreadTunnelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MainThreadTunnelServiceShape, MainContext, PortAttributesProviderSelector } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import type * as vscode from 'vscode';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
@@ -13,14 +13,16 @@ import { exec } from 'child_process';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as fs from 'fs';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as types from 'vs/workbench/api/common/extHostTypes';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { IExtHostTunnelService, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { TunnelOptions, TunnelCreationOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward } from 'vs/platform/remote/common/tunnel';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { MovingAverage } from 'vs/base/common/numbers';
|
||||
import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
|
||||
class ExtensionTunnel implements vscode.Tunnel {
|
||||
private _onDispose: Emitter<void> = new Emitter();
|
||||
@@ -37,7 +39,7 @@ class ExtensionTunnel implements vscode.Tunnel {
|
||||
}
|
||||
}
|
||||
|
||||
export function getSockets(stdout: string): { pid: number, socket: number }[] {
|
||||
export function getSockets(stdout: string): Record<string, { pid: number; socket: number; }> {
|
||||
const lines = stdout.trim().split('\n');
|
||||
const mapped: { pid: number, socket: number }[] = [];
|
||||
lines.forEach(line => {
|
||||
@@ -49,7 +51,11 @@ export function getSockets(stdout: string): { pid: number, socket: number }[] {
|
||||
});
|
||||
}
|
||||
});
|
||||
return mapped;
|
||||
const socketMap = mapped.reduce((m, socket) => {
|
||||
m[socket.socket] = socket;
|
||||
return m;
|
||||
}, {} as Record<string, typeof mapped[0]>);
|
||||
return socketMap;
|
||||
}
|
||||
|
||||
export function loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] {
|
||||
@@ -106,29 +112,70 @@ function knownExcludeCmdline(command: string): boolean {
|
||||
|| (command.indexOf('_productName=VSCode') !== -1);
|
||||
}
|
||||
|
||||
export async function findPorts(tcp: string, tcp6: string, procSockets: string, processes: { pid: number, cwd: string, cmd: string }[]): Promise<CandidatePort[]> {
|
||||
const connections: { socket: number, ip: string, port: number }[] = loadListeningPorts(tcp, tcp6);
|
||||
const sockets = getSockets(procSockets);
|
||||
export function getRootProcesses(stdout: string) {
|
||||
const lines = stdout.trim().split('\n');
|
||||
const mapped: { pid: number, cmd: string, ppid: number }[] = [];
|
||||
lines.forEach(line => {
|
||||
const match = /^\d+\s+\D+\s+root\s+(\d+)\s+(\d+).+\d+\:\d+\:\d+\s+(.+)$/.exec(line)!;
|
||||
if (match && match.length >= 4) {
|
||||
mapped.push({
|
||||
pid: parseInt(match[1], 10),
|
||||
ppid: parseInt(match[2]),
|
||||
cmd: match[3]
|
||||
});
|
||||
}
|
||||
});
|
||||
return mapped;
|
||||
}
|
||||
|
||||
const socketMap = sockets.reduce((m, socket) => {
|
||||
m[socket.socket] = socket;
|
||||
return m;
|
||||
}, {} as Record<string, typeof sockets[0]>);
|
||||
export async function findPorts(connections: { socket: number, ip: string, port: number }[], socketMap: Record<string, { pid: number, socket: number }>, processes: { pid: number, cwd: string, cmd: string }[]): Promise<CandidatePort[]> {
|
||||
const processMap = processes.reduce((m, process) => {
|
||||
m[process.pid] = process;
|
||||
return m;
|
||||
}, {} as Record<string, typeof processes[0]>);
|
||||
|
||||
const ports: CandidatePort[] = [];
|
||||
connections.filter((connection => socketMap[connection.socket])).forEach(({ socket, ip, port }) => {
|
||||
const command = processMap[socketMap[socket].pid].cmd;
|
||||
if (!knownExcludeCmdline(command)) {
|
||||
ports.push({ host: ip, port, detail: processMap[socketMap[socket].pid].cmd, pid: socketMap[socket].pid });
|
||||
connections.forEach(({ socket, ip, port }) => {
|
||||
const pid = socketMap[socket] ? socketMap[socket].pid : undefined;
|
||||
const command: string | undefined = pid ? processMap[pid]?.cmd : undefined;
|
||||
if (pid && command && !knownExcludeCmdline(command)) {
|
||||
ports.push({ host: ip, port, detail: command, pid });
|
||||
}
|
||||
});
|
||||
return ports;
|
||||
}
|
||||
|
||||
export function tryFindRootPorts(connections: { socket: number, ip: string, port: number }[], rootProcessesStdout: string, previousPorts: Map<number, CandidatePort & { ppid: number }>): Map<number, CandidatePort & { ppid: number }> {
|
||||
const ports: Map<number, CandidatePort & { ppid: number }> = new Map();
|
||||
const rootProcesses = getRootProcesses(rootProcessesStdout);
|
||||
|
||||
for (const connection of connections) {
|
||||
const previousPort = previousPorts.get(connection.port);
|
||||
if (previousPort) {
|
||||
ports.set(connection.port, previousPort);
|
||||
continue;
|
||||
}
|
||||
const rootProcessMatch = rootProcesses.find((value) => value.cmd.includes(`${connection.port}`));
|
||||
if (rootProcessMatch) {
|
||||
let bestMatch = rootProcessMatch;
|
||||
// There are often several processes that "look" like they could match the port.
|
||||
// The one we want is usually the child of the other. Find the most child process.
|
||||
let mostChild: { pid: number, cmd: string, ppid: number } | undefined;
|
||||
do {
|
||||
mostChild = rootProcesses.find(value => value.ppid === bestMatch.pid);
|
||||
if (mostChild) {
|
||||
bestMatch = mostChild;
|
||||
}
|
||||
} while (mostChild);
|
||||
ports.set(connection.port, { host: connection.ip, port: connection.port, pid: bestMatch.pid, detail: bestMatch.cmd, ppid: bestMatch.ppid });
|
||||
} else {
|
||||
ports.set(connection.port, { host: connection.ip, port: connection.port, ppid: Number.MAX_VALUE });
|
||||
}
|
||||
}
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService {
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly _proxy: MainThreadTunnelServiceShape;
|
||||
@@ -138,6 +185,10 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
private _onDidChangeTunnels: Emitter<void> = new Emitter<void>();
|
||||
onDidChangeTunnels: vscode.Event<void> = this._onDidChangeTunnels.event;
|
||||
private _candidateFindingEnabled: boolean = false;
|
||||
private _foundRootPorts: Map<number, CandidatePort & { ppid: number }> = new Map();
|
||||
|
||||
private _providerHandleCounter: number = 0;
|
||||
private _portAttributesProviders: Map<number, { provider: vscode.PortAttributesProvider, selector: PortAttributesProviderSelector }> = new Map();
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@@ -152,6 +203,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
}
|
||||
|
||||
async openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) ${extension.identifier} called openTunnel API for ${forward.remoteAddress.port}.`);
|
||||
const tunnel = await this._proxy.$openTunnel(forward, extension.displayName);
|
||||
if (tunnel) {
|
||||
const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remoteAddress, tunnel.localAddress, () => {
|
||||
@@ -172,6 +224,40 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
return Math.max(movingAverage * 20, 2000);
|
||||
}
|
||||
|
||||
private nextPortAttributesProviderHandle(): number {
|
||||
return this._providerHandleCounter++;
|
||||
}
|
||||
|
||||
registerPortsAttributesProvider(portSelector: PortAttributesProviderSelector, provider: vscode.PortAttributesProvider): vscode.Disposable {
|
||||
const providerHandle = this.nextPortAttributesProviderHandle();
|
||||
this._portAttributesProviders.set(providerHandle, { selector: portSelector, provider });
|
||||
|
||||
this._proxy.$registerPortsAttributesProvider(portSelector, providerHandle);
|
||||
return new types.Disposable(() => {
|
||||
this._portAttributesProviders.delete(providerHandle);
|
||||
this._proxy.$unregisterPortsAttributesProvider(providerHandle);
|
||||
});
|
||||
}
|
||||
|
||||
async $providePortAttributes(handles: number[], ports: number[], pid: number | undefined, commandline: string | undefined, cancellationToken: vscode.CancellationToken): Promise<ProvidedPortAttributes[]> {
|
||||
const providedAttributes = await Promise.all(handles.map(handle => {
|
||||
const provider = this._portAttributesProviders.get(handle);
|
||||
if (!provider) {
|
||||
return [];
|
||||
}
|
||||
return provider.provider.providePortAttributes(ports, pid, commandline, cancellationToken);
|
||||
}));
|
||||
|
||||
const allAttributes = <vscode.PortAttributes[][]>providedAttributes.filter(attribute => !!attribute && attribute.length > 0);
|
||||
|
||||
return (allAttributes.length > 0) ? flatten(allAttributes).map(attributes => {
|
||||
return {
|
||||
autoForwardAction: <ProvidedOnAutoForward><unknown>attributes.autoForwardAction,
|
||||
port: attributes.port
|
||||
};
|
||||
}) : [];
|
||||
}
|
||||
|
||||
async $registerCandidateFinder(enable: boolean): Promise<void> {
|
||||
if (enable && this._candidateFindingEnabled) {
|
||||
// already enabled
|
||||
@@ -180,10 +266,11 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
this._candidateFindingEnabled = enable;
|
||||
// Regularly scan to see if the candidate ports have changed.
|
||||
let movingAverage = new MovingAverage();
|
||||
let oldPorts: { host: string, port: number, detail: string }[] | undefined = undefined;
|
||||
let oldPorts: { host: string, port: number, detail?: string }[] | undefined = undefined;
|
||||
while (this._candidateFindingEnabled) {
|
||||
const startTime = new Date().getTime();
|
||||
const newPorts = await this.findCandidatePorts();
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) found candidate ports ${newPorts.map(port => port.port).join(', ')}`);
|
||||
const timeTaken = new Date().getTime() - startTime;
|
||||
movingAverage.update(timeTaken);
|
||||
if (!oldPorts || (JSON.stringify(oldPorts) !== JSON.stringify(newPorts))) {
|
||||
@@ -238,31 +325,36 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
async $forwardPort(tunnelOptions: TunnelOptions, tunnelCreationOptions: TunnelCreationOptions): Promise<TunnelDto | undefined> {
|
||||
if (this._forwardPortProvider) {
|
||||
try {
|
||||
this.logService.trace('$forwardPort: Getting tunnel from provider.');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Getting tunnel from provider.');
|
||||
const providedPort = this._forwardPortProvider(tunnelOptions, tunnelCreationOptions);
|
||||
this.logService.trace('$forwardPort: Got tunnel promise from provider.');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Got tunnel promise from provider.');
|
||||
if (providedPort !== undefined) {
|
||||
const tunnel = await providedPort;
|
||||
this.logService.trace('$forwardPort: Successfully awaited tunnel from provider.');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Successfully awaited tunnel from provider.');
|
||||
if (!this._extensionTunnels.has(tunnelOptions.remoteAddress.host)) {
|
||||
this._extensionTunnels.set(tunnelOptions.remoteAddress.host, new Map());
|
||||
}
|
||||
const disposeListener = this._register(tunnel.onDidDispose(() => this._proxy.$closeTunnel(tunnel.remoteAddress)));
|
||||
const disposeListener = this._register(tunnel.onDidDispose(() => {
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Extension fired tunnel\'s onDidDispose.');
|
||||
return this._proxy.$closeTunnel(tunnel.remoteAddress);
|
||||
}));
|
||||
this._extensionTunnels.get(tunnelOptions.remoteAddress.host)!.set(tunnelOptions.remoteAddress.port, { tunnel, disposeListener });
|
||||
return TunnelDto.fromApiTunnel(tunnel);
|
||||
} else {
|
||||
this.logService.trace('$forwardPort: Tunnel is undefined');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) Tunnel is undefined');
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.trace('$forwardPort: tunnel provider error');
|
||||
this.logService.trace('ForwardedPorts: (ExtHostTunnelService) tunnel provider error');
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async $applyCandidateFilter(candidates: CandidatePort[]): Promise<CandidatePort[]> {
|
||||
const filter = await Promise.all(candidates.map(candidate => this._showCandidatePort(candidate.host, candidate.port, candidate.detail)));
|
||||
return candidates.filter((candidate, index) => filter[index]);
|
||||
const filter = await Promise.all(candidates.map(candidate => this._showCandidatePort(candidate.host, candidate.port, candidate.detail ?? '')));
|
||||
const result = candidates.filter((candidate, index) => filter[index]);
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) filtered from ${candidates.map(port => port.port).join(', ')} to ${result.map(port => port.port).join(', ')}`);
|
||||
return result;
|
||||
}
|
||||
|
||||
async findCandidatePorts(): Promise<CandidatePort[]> {
|
||||
@@ -274,11 +366,14 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
} catch (e) {
|
||||
// File reading error. No additional handling needed.
|
||||
}
|
||||
const connections: { socket: number, ip: string, port: number }[] = loadListeningPorts(tcp, tcp6);
|
||||
|
||||
const procSockets: string = await (new Promise(resolve => {
|
||||
exec('ls -l /proc/[0-9]*/fd/[0-9]* | grep socket:', (error, stdout, stderr) => {
|
||||
resolve(stdout);
|
||||
});
|
||||
}));
|
||||
const socketMap = getSockets(procSockets);
|
||||
|
||||
const procChildren = await pfs.readdir('/proc');
|
||||
const processes: {
|
||||
@@ -298,6 +393,36 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
|
||||
//
|
||||
}
|
||||
}
|
||||
return findPorts(tcp, tcp6, procSockets, processes);
|
||||
|
||||
const unFoundConnections: { socket: number, ip: string, port: number }[] = [];
|
||||
const filteredConnections = connections.filter((connection => {
|
||||
const foundConnection = socketMap[connection.socket];
|
||||
if (!foundConnection) {
|
||||
unFoundConnections.push(connection);
|
||||
}
|
||||
return foundConnection;
|
||||
}));
|
||||
|
||||
const foundPorts = findPorts(filteredConnections, socketMap, processes);
|
||||
let heuristicPorts: CandidatePort[] | undefined;
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) number of possible root ports ${unFoundConnections.length}`);
|
||||
if (unFoundConnections.length > 0) {
|
||||
const rootProcesses: string = await (new Promise(resolve => {
|
||||
exec('ps -F -A -l | grep root', (error, stdout, stderr) => {
|
||||
resolve(stdout);
|
||||
});
|
||||
}));
|
||||
this._foundRootPorts = tryFindRootPorts(unFoundConnections, rootProcesses, this._foundRootPorts);
|
||||
heuristicPorts = Array.from(this._foundRootPorts.values());
|
||||
this.logService.trace(`ForwardedPorts: (ExtHostTunnelService) heuristic ports ${heuristicPorts.join(', ')}`);
|
||||
|
||||
}
|
||||
return foundPorts.then(foundCandidates => {
|
||||
if (heuristicPorts) {
|
||||
return foundCandidates.concat(heuristicPorts);
|
||||
} else {
|
||||
return foundCandidates;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterc
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionRuntime } from 'vs/workbench/api/common/extHostTypes';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { MainContext, MainThreadConsoleShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
|
||||
namespace TrustedFunction {
|
||||
|
||||
@@ -68,6 +69,9 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
private _fakeModules?: WorkerRequireInterceptor;
|
||||
|
||||
protected async _beforeAlmostReadyToRunExtensions(): Promise<void> {
|
||||
const mainThreadConsole = this._extHostContext.getProxy(MainContext.MainThreadConsole);
|
||||
wrapConsoleMethods(mainThreadConsole);
|
||||
|
||||
// initialize API and register actors
|
||||
const apiFactory = this._instaService.invokeFunction(createApiFactoryAndRegisterActors);
|
||||
this._fakeModules = this._instaService.createInstance(WorkerRequireInterceptor, apiFactory, this._registry);
|
||||
@@ -167,3 +171,74 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
function ensureSuffix(path: string, suffix: string): string {
|
||||
return path.endsWith(suffix) ? path : path + suffix;
|
||||
}
|
||||
|
||||
// copied from bootstrap-fork.js
|
||||
function wrapConsoleMethods(service: MainThreadConsoleShape) {
|
||||
wrap('info', 'log');
|
||||
wrap('log', 'log');
|
||||
wrap('warn', 'warn');
|
||||
wrap('error', 'error');
|
||||
|
||||
function wrap(method: 'error' | 'warn' | 'info' | 'log', severity: 'error' | 'warn' | 'log') {
|
||||
console[method] = function () {
|
||||
service.$logExtensionHostMessage({ type: '__$console', severity, arguments: safeToArray(arguments) });
|
||||
};
|
||||
}
|
||||
|
||||
const MAX_LENGTH = 100000;
|
||||
|
||||
function safeToArray(args: IArguments) {
|
||||
const seen: any[] = [];
|
||||
const argsArray = [];
|
||||
|
||||
// Massage some arguments with special treatment
|
||||
if (args.length) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
|
||||
// Any argument of type 'undefined' needs to be specially treated because
|
||||
// JSON.stringify will simply ignore those. We replace them with the string
|
||||
// 'undefined' which is not 100% right, but good enough to be logged to console
|
||||
if (typeof args[i] === 'undefined') {
|
||||
args[i] = 'undefined';
|
||||
}
|
||||
|
||||
// Any argument that is an Error will be changed to be just the error stack/message
|
||||
// itself because currently cannot serialize the error over entirely.
|
||||
else if (args[i] instanceof Error) {
|
||||
const errorObj = args[i];
|
||||
if (errorObj.stack) {
|
||||
args[i] = errorObj.stack;
|
||||
} else {
|
||||
args[i] = errorObj.toString();
|
||||
}
|
||||
}
|
||||
|
||||
argsArray.push(args[i]);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const res = JSON.stringify(argsArray, function (key, value) {
|
||||
|
||||
// Objects get special treatment to prevent circles
|
||||
if (value && typeof value === 'object') {
|
||||
if (seen.indexOf(value) !== -1) {
|
||||
return '[Circular]';
|
||||
}
|
||||
|
||||
seen.push(value);
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
|
||||
if (res.length > MAX_LENGTH) {
|
||||
return 'Output omitted for a large object that exceeds the limits';
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch (error) {
|
||||
return `Output omitted for an object that cannot be inspected ('${error.toString()}')`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user