mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-01 05:51:32 +01:00
Merge branch 'master' into joh/celldocs
This commit is contained in:
@@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import * as nls from 'vs/nls';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { IAuthenticationService, AllowedExtension, readAllowedExtensions } from 'vs/workbench/services/authentication/browser/authenticationService';
|
||||
import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService';
|
||||
import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
@@ -17,6 +17,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { fromNow } from 'vs/base/common/date';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Platform, platform } from 'vs/base/common/platform';
|
||||
|
||||
const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline', 'vscode.github-browser'];
|
||||
|
||||
@@ -213,7 +215,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
|
||||
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
|
||||
@@ -245,6 +248,10 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
this.authenticationService.unregisterAuthenticationProvider(id);
|
||||
}
|
||||
|
||||
$ensureProvider(id: string): Promise<void> {
|
||||
return this.extensionService.activateByEvent(getAuthenticationProviderActivationEvent(id));
|
||||
}
|
||||
|
||||
$sendDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void {
|
||||
this.authenticationService.sessionsUpdate(id, event);
|
||||
}
|
||||
@@ -292,7 +299,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
}
|
||||
|
||||
const session = await this.authenticationService.login(providerId, scopes);
|
||||
await this.$setTrustedExtension(providerId, session.account.label, extensionId, extensionName);
|
||||
await this.$setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
|
||||
return session;
|
||||
} else {
|
||||
await this.$requestNewSession(providerId, scopes, extensionId, extensionName);
|
||||
@@ -386,7 +393,11 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
}
|
||||
|
||||
const remoteConnection = this.remoteAgentService.getConnection();
|
||||
if (remoteConnection && remoteConnection.remoteAuthority && remoteConnection.remoteAuthority.startsWith('vsonline') && VSO_ALLOWED_EXTENSIONS.includes(extensionId)) {
|
||||
const isVSO = remoteConnection !== null
|
||||
? remoteConnection.remoteAuthority.startsWith('vsonline')
|
||||
: platform === Platform.Web;
|
||||
|
||||
if (isVSO && VSO_ALLOWED_EXTENSIONS.includes(extensionId)) {
|
||||
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
|
||||
return true;
|
||||
}
|
||||
@@ -423,11 +434,13 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
|
||||
return choice === 0;
|
||||
}
|
||||
|
||||
async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void> {
|
||||
async $setTrustedExtensionAndAccountPreference(providerId: string, accountName: string, extensionId: string, extensionName: string, sessionId: string): Promise<void> {
|
||||
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
|
||||
if (!allowList.find(allowed => allowed.id === extensionId)) {
|
||||
allowList.push({ id: extensionId, name: extensionName });
|
||||
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,11 +228,13 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
public $startDebugging(folder: UriComponents | undefined, nameOrConfig: string | IDebugConfiguration, options: IStartDebuggingOptions): Promise<boolean> {
|
||||
const folderUri = folder ? uri.revive(folder) : undefined;
|
||||
const launch = this.debugService.getConfigurationManager().getLaunch(folderUri);
|
||||
const parentSession = this.getSession(options.parentSessionID);
|
||||
const debugOptions: IDebugSessionOptions = {
|
||||
noDebug: options.noDebug,
|
||||
parentSession: this.getSession(options.parentSessionID),
|
||||
parentSession,
|
||||
repl: options.repl,
|
||||
compact: options.compact
|
||||
compact: options.compact,
|
||||
compoundRoot: parentSession?.compoundRoot
|
||||
};
|
||||
return this.debugService.startDebugging(launch, nameOrConfig, debugOptions).then(success => {
|
||||
return success;
|
||||
@@ -262,14 +264,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
|
||||
return Promise.reject(new Error('debug session not found'));
|
||||
}
|
||||
|
||||
public $terminateDebugSession(sessionId: DebugSessionUUID): Promise<void> {
|
||||
const session = this.debugService.getModel().getSession(sessionId, true);
|
||||
if (session) {
|
||||
return session.terminate();
|
||||
}
|
||||
return Promise.reject(new Error('debug session not found'));
|
||||
}
|
||||
|
||||
public $stopDebugging(sessionId: DebugSessionUUID | undefined): Promise<void> {
|
||||
if (sessionId) {
|
||||
const session = this.debugService.getModel().getSession(sessionId, true);
|
||||
|
||||
@@ -269,6 +269,14 @@ export class MainThreadTextEditor {
|
||||
}
|
||||
}));
|
||||
|
||||
const isValidCodeEditor = () => {
|
||||
// Due to event timings, it is possible that there is a model change event not yet delivered to us.
|
||||
// > e.g. a model change event is emitted to a listener which then decides to update editor options
|
||||
// > In this case the editor configuration change event reaches us first.
|
||||
// So simply check that the model is still attached to this code editor
|
||||
return (this._codeEditor && this._codeEditor.getModel() === this._model);
|
||||
};
|
||||
|
||||
const updateProperties = (selectionChangeSource: string | null) => {
|
||||
// Some editor events get delivered faster than model content changes. This is
|
||||
// problematic, as this leads to editor properties reaching the extension host
|
||||
@@ -287,18 +295,30 @@ export class MainThreadTextEditor {
|
||||
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidChangeCursorSelection((e) => {
|
||||
// selection
|
||||
if (!isValidCodeEditor()) {
|
||||
return;
|
||||
}
|
||||
updateProperties(e.source);
|
||||
}));
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration(() => {
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidChangeConfiguration((e) => {
|
||||
// options
|
||||
if (!isValidCodeEditor()) {
|
||||
return;
|
||||
}
|
||||
updateProperties(null);
|
||||
}));
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidLayoutChange(() => {
|
||||
// visibleRanges
|
||||
if (!isValidCodeEditor()) {
|
||||
return;
|
||||
}
|
||||
updateProperties(null);
|
||||
}));
|
||||
this._codeEditorListeners.add(this._codeEditor.onDidScrollChange(() => {
|
||||
// visibleRanges
|
||||
if (!isValidCodeEditor()) {
|
||||
return;
|
||||
}
|
||||
updateProperties(null);
|
||||
}));
|
||||
this._updatePropertiesNow(null);
|
||||
|
||||
@@ -128,7 +128,7 @@ export class MainThreadExtensionService implements MainThreadExtensionServiceSha
|
||||
}
|
||||
}
|
||||
|
||||
$onExtensionHostExit(code: number): void {
|
||||
async $onExtensionHostExit(code: number): Promise<void> {
|
||||
this._extensionService._onExtensionHostExit(code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext, INotebookDocumentsAndEditorsDelta, INotebookModelAddedData } from '../common/extHost.protocol';
|
||||
import { Disposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IExtHostContext, ExtHostNotebookShape, ExtHostContext, INotebookDocumentsAndEditorsDelta } from '../common/extHost.protocol';
|
||||
import { Disposable, IDisposable, combinedDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/common/notebookService';
|
||||
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, INotebookTextModelBackup, IEditor, INotebookRendererInfo, IOutputRenderRequest, IOutputRenderResponse, INotebookDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation, ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellEditType, CellKind, INotebookKernelInfo, INotebookKernelInfoDto, IEditor, INotebookRendererInfo, IOutputRenderRequest, IOutputRenderResponse, INotebookDocumentFilter } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -19,8 +18,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { IRelativePattern } from 'vs/base/common/glob';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IUndoRedoService, UndoRedoElementType } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
@@ -55,42 +53,8 @@ export class MainThreadNotebookDocument extends Disposable {
|
||||
}));
|
||||
}
|
||||
|
||||
async applyEdit(modelVersionId: number, edits: ICellEditOperation[], emitToExtHost: boolean, synchronous: boolean): Promise<boolean> {
|
||||
await this.notebookService.transformEditsOutputs(this.textModel, edits);
|
||||
if (synchronous) {
|
||||
return this._textModel.$applyEdit(modelVersionId, edits, emitToExtHost, synchronous);
|
||||
} else {
|
||||
return new Promise(resolve => {
|
||||
this._register(DOM.scheduleAtNextAnimationFrame(() => {
|
||||
const ret = this._textModel.$applyEdit(modelVersionId, edits, emitToExtHost, true);
|
||||
resolve(ret);
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async spliceNotebookCellOutputs(cellHandle: number, splices: NotebookCellOutputsSplice[]) {
|
||||
await this.notebookService.transformSpliceOutputs(this.textModel, splices);
|
||||
this._textModel.$spliceNotebookCellOutputs(cellHandle, splices);
|
||||
}
|
||||
|
||||
handleEdit(editId: number, label: string | undefined): void {
|
||||
this.undoRedoService.pushElement({
|
||||
type: UndoRedoElementType.Resource,
|
||||
resource: this._textModel.uri,
|
||||
label: label ?? nls.localize('defaultEditLabel', "Edit"),
|
||||
undo: async () => {
|
||||
await this._proxy.$undoNotebook(this._textModel.viewType, this._textModel.uri, editId, this._textModel.isDirty);
|
||||
},
|
||||
redo: async () => {
|
||||
await this._proxy.$redoNotebook(this._textModel.viewType, this._textModel.uri, editId, this._textModel.isDirty);
|
||||
},
|
||||
});
|
||||
this._textModel.setDirty(true);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this._textModel.dispose();
|
||||
// this._textModel.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
@@ -203,21 +167,21 @@ class DocumentAndEditorState {
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadNotebook)
|
||||
export class MainThreadNotebooks extends Disposable implements MainThreadNotebookShape {
|
||||
private readonly _notebookProviders = new Map<string, MainThreadNotebookController>();
|
||||
private readonly _notebookProviders = new Map<string, IMainNotebookController>();
|
||||
private readonly _notebookKernels = new Map<string, MainThreadNotebookKernel>();
|
||||
private readonly _notebookKernelProviders = new Map<number, { extension: NotebookExtensionDescription, emitter: Emitter<void>, provider: IDisposable }>();
|
||||
private readonly _notebookRenderers = new Map<string, MainThreadNotebookRenderer>();
|
||||
private readonly _proxy: ExtHostNotebookShape;
|
||||
private _toDisposeOnEditorRemove = new Map<string, IDisposable>();
|
||||
private _currentState?: DocumentAndEditorState;
|
||||
private _editorEventListenersMapping: Map<string, DisposableStore> = new Map();
|
||||
|
||||
constructor(
|
||||
extHostContext: IExtHostContext,
|
||||
@INotebookService private _notebookService: INotebookService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
|
||||
|
||||
) {
|
||||
super();
|
||||
@@ -226,15 +190,23 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
|
||||
async $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
|
||||
if (controller) {
|
||||
return controller.tryApplyEdits(resource, modelVersionId, edits, renderers);
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
if (textModel) {
|
||||
await this._notebookService.transformEditsOutputs(textModel, edits);
|
||||
return textModel.$applyEdit(modelVersionId, edits, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async removeNotebookTextModel(uri: URI): Promise<void> {
|
||||
// TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together
|
||||
await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [uri] });
|
||||
let textModelDisposableStore = this._editorEventListenersMapping.get(uri.toString());
|
||||
textModelDisposableStore?.dispose();
|
||||
this._editorEventListenersMapping.delete(URI.from(uri).toString());
|
||||
}
|
||||
|
||||
private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) {
|
||||
if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
|
||||
return false;
|
||||
@@ -300,14 +272,43 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
this._removeNotebookEditor(editors);
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onNotebookDocumentAdd(() => {
|
||||
this._register(this._notebookService.onNotebookDocumentAdd((documents) => {
|
||||
documents.forEach(doc => {
|
||||
if (!this._editorEventListenersMapping.has(doc.toString())) {
|
||||
const disposableStore = new DisposableStore();
|
||||
const textModel = this._notebookService.getNotebookTextModel(doc);
|
||||
disposableStore.add(textModel!.onDidModelChangeProxy(e => {
|
||||
this._proxy.$acceptModelChanged(textModel!.uri, e);
|
||||
this._proxy.$acceptEditorPropertiesChanged(doc, { selections: { selections: textModel!.selections }, metadata: null });
|
||||
}));
|
||||
disposableStore.add(textModel!.onDidSelectionChange(e => {
|
||||
const selectionsChange = e ? { selections: e } : null;
|
||||
this._proxy.$acceptEditorPropertiesChanged(doc, { selections: selectionsChange, metadata: null });
|
||||
}));
|
||||
|
||||
this._editorEventListenersMapping.set(textModel!.uri.toString(), disposableStore);
|
||||
}
|
||||
});
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onNotebookDocumentRemove(() => {
|
||||
this._register(this._notebookService.onNotebookDocumentRemove((documents) => {
|
||||
documents.forEach(doc => {
|
||||
this._editorEventListenersMapping.get(doc.toString())?.dispose();
|
||||
this._editorEventListenersMapping.delete(doc.toString());
|
||||
});
|
||||
|
||||
this._updateState();
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onDidChangeNotebookActiveKernel(e => {
|
||||
this._proxy.$acceptNotebookActiveKernelChange(e);
|
||||
}));
|
||||
|
||||
this._register(this._notebookService.onNotebookDocumentSaved(e => {
|
||||
this._proxy.$acceptModelSaved(e);
|
||||
}));
|
||||
|
||||
const updateOrder = () => {
|
||||
let userOrder = this.configurationService.getValue<string[]>('notebook.displayOrder');
|
||||
this._proxy.$acceptDisplayOrder({
|
||||
@@ -333,10 +334,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
this._updateState(notebookEditor);
|
||||
}
|
||||
|
||||
async addNotebookDocument(data: INotebookModelAddedData) {
|
||||
this._updateState();
|
||||
}
|
||||
|
||||
private _addNotebookEditor(e: IEditor) {
|
||||
this._toDisposeOnEditorRemove.set(e.getId(), combinedDisposable(
|
||||
e.onDidChangeModel(() => this._updateState()),
|
||||
@@ -425,18 +422,92 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
this._notebookService.unregisterNotebookRenderer(id);
|
||||
}
|
||||
|
||||
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernel: INotebookKernelInfoDto | undefined): Promise<void> {
|
||||
let controller = new MainThreadNotebookController(this._proxy, this, viewType, supportBackup, kernel, this._notebookService, this._instantiationService);
|
||||
this._notebookProviders.set(viewType, controller);
|
||||
this._notebookService.registerNotebookController(viewType, extension, controller);
|
||||
async $registerNotebookProvider(_extension: NotebookExtensionDescription, _viewType: string, _supportBackup: boolean, _kernel: INotebookKernelInfoDto | undefined): Promise<void> {
|
||||
const controller: IMainNotebookController = {
|
||||
kernel: _kernel,
|
||||
supportBackup: _supportBackup,
|
||||
reloadNotebook: async (mainthreadTextModel: NotebookTextModel) => {
|
||||
const data = await this._proxy.$resolveNotebookData(_viewType, mainthreadTextModel.uri);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
mainthreadTextModel.languages = data.languages;
|
||||
mainthreadTextModel.metadata = data.metadata;
|
||||
|
||||
const edits: ICellEditOperation[] = [
|
||||
{ editType: CellEditType.Delete, count: mainthreadTextModel.cells.length, index: 0 },
|
||||
{ editType: CellEditType.Insert, index: 0, cells: data.cells }
|
||||
];
|
||||
|
||||
await this._notebookService.transformEditsOutputs(mainthreadTextModel, edits);
|
||||
await new Promise(resolve => {
|
||||
DOM.scheduleAtNextAnimationFrame(() => {
|
||||
const ret = mainthreadTextModel!.$applyEdit(mainthreadTextModel!.versionId, edits, true);
|
||||
resolve(ret);
|
||||
});
|
||||
});
|
||||
},
|
||||
createNotebook: async (textModel: NotebookTextModel, backupId?: string) => {
|
||||
// open notebook document
|
||||
const data = await this._proxy.$resolveNotebookData(textModel.viewType, textModel.uri, backupId);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
textModel.languages = data.languages;
|
||||
textModel.metadata = data.metadata;
|
||||
|
||||
if (data.cells.length) {
|
||||
textModel.initialize(data!.cells);
|
||||
} else {
|
||||
const mainCell = textModel.createCellTextModel([''], textModel.languages.length ? textModel.languages[0] : '', CellKind.Code, [], undefined);
|
||||
textModel.insertTemplateCell(mainCell);
|
||||
}
|
||||
|
||||
this._proxy.$acceptEditorPropertiesChanged(textModel.uri, { selections: null, metadata: textModel.metadata });
|
||||
return;
|
||||
},
|
||||
resolveNotebookEditor: async (viewType: string, uri: URI, editorId: string) => {
|
||||
await this._proxy.$resolveNotebookEditor(viewType, uri, editorId);
|
||||
},
|
||||
executeNotebookByAttachedKernel: async (viewType: string, uri: URI) => {
|
||||
return this.executeNotebookByAttachedKernel(viewType, uri);
|
||||
},
|
||||
cancelNotebookByAttachedKernel: async (viewType: string, uri: URI) => {
|
||||
return this.cancelNotebookByAttachedKernel(viewType, uri);
|
||||
},
|
||||
onDidReceiveMessage: (editorId: string, rendererType: string | undefined, message: unknown) => {
|
||||
this._proxy.$onDidReceiveMessage(editorId, rendererType, message);
|
||||
},
|
||||
removeNotebookDocument: async (uri: URI) => {
|
||||
return this.removeNotebookTextModel(uri);
|
||||
},
|
||||
executeNotebookCell: async (uri: URI, handle: number) => {
|
||||
return this._proxy.$executeNotebookByAttachedKernel(_viewType, uri, handle);
|
||||
},
|
||||
cancelNotebookCell: async (uri: URI, handle: number) => {
|
||||
return this._proxy.$cancelNotebookByAttachedKernel(_viewType, uri, handle);
|
||||
},
|
||||
save: async (uri: URI, token: CancellationToken) => {
|
||||
return this._proxy.$saveNotebook(_viewType, uri, token);
|
||||
},
|
||||
saveAs: async (uri: URI, target: URI, token: CancellationToken) => {
|
||||
return this._proxy.$saveNotebookAs(_viewType, uri, target, token);
|
||||
},
|
||||
backup: async (uri: URI, token: CancellationToken) => {
|
||||
return this._proxy.$backup(_viewType, uri, token);
|
||||
}
|
||||
};
|
||||
|
||||
this._notebookProviders.set(_viewType, controller);
|
||||
this._notebookService.registerNotebookController(_viewType, _extension, controller);
|
||||
return;
|
||||
}
|
||||
|
||||
async $onNotebookChange(viewType: string, uri: UriComponents): Promise<void> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
if (controller) {
|
||||
controller.handleNotebookChange(uri);
|
||||
}
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(uri));
|
||||
textModel?.handleUnknownChange();
|
||||
}
|
||||
|
||||
async $unregisterNotebookProvider(viewType: string): Promise<void> {
|
||||
@@ -462,17 +533,28 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
const emitter = new Emitter<void>();
|
||||
const that = this;
|
||||
const provider = this._notebookService.registerNotebookKernelProvider({
|
||||
providerExtensionId: extension.id.value,
|
||||
providerDescription: extension.description,
|
||||
onDidChangeKernels: emitter.event,
|
||||
selector: documentFilter,
|
||||
provideKernels: (uri: URI, token: CancellationToken) => {
|
||||
return that._proxy.$provideNotebookKernels(handle, uri, token);
|
||||
provideKernels: async (uri: URI, token: CancellationToken) => {
|
||||
const kernels = await that._proxy.$provideNotebookKernels(handle, uri, token);
|
||||
return kernels.map(kernel => {
|
||||
return {
|
||||
...kernel,
|
||||
providerHandle: handle
|
||||
};
|
||||
});
|
||||
},
|
||||
resolveKernel: (editorId: string, uri: URI, kernelId: string, token: CancellationToken) => {
|
||||
return that._proxy.$resolveNotebookKernel(handle, editorId, uri, kernelId, token);
|
||||
},
|
||||
executeNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined, token: CancellationToken) => {
|
||||
return that._proxy.$executeNotebookKernelFromProvider(handle, uri, kernelId, cellHandle, token);
|
||||
}
|
||||
executeNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => {
|
||||
return that._proxy.$executeNotebookKernelFromProvider(handle, uri, kernelId, cellHandle);
|
||||
},
|
||||
cancelNotebook: (uri: URI, kernelId: string, cellHandle: number | undefined) => {
|
||||
return that._proxy.$cancelNotebookKernelFromProvider(handle, uri, kernelId, cellHandle);
|
||||
},
|
||||
});
|
||||
this._notebookKernelProviders.set(handle, {
|
||||
extension,
|
||||
@@ -500,36 +582,35 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
|
||||
async $updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
|
||||
if (controller) {
|
||||
controller.updateLanguages(resource, languages);
|
||||
}
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
textModel?.updateLanguages(languages);
|
||||
}
|
||||
|
||||
async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise<void> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
|
||||
if (controller) {
|
||||
controller.updateNotebookMetadata(resource, metadata);
|
||||
}
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
textModel?.updateNotebookMetadata(metadata);
|
||||
}
|
||||
|
||||
async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise<void> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
|
||||
if (controller) {
|
||||
controller.updateNotebookCellMetadata(resource, handle, metadata);
|
||||
}
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
textModel?.updateNotebookCellMetadata(handle, metadata);
|
||||
}
|
||||
|
||||
async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
await controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers);
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
|
||||
if (textModel) {
|
||||
await this._notebookService.transformSpliceOutputs(textModel, splices);
|
||||
textModel.$spliceNotebookCellOutputs(cellHandle, splices);
|
||||
}
|
||||
}
|
||||
|
||||
async executeNotebookByAttachedKernel(viewType: string, uri: URI, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$executeNotebookByAttachedKernel(viewType, uri, undefined, token);
|
||||
async executeNotebookByAttachedKernel(viewType: string, uri: URI): Promise<void> {
|
||||
return this._proxy.$executeNotebookByAttachedKernel(viewType, uri, undefined);
|
||||
}
|
||||
|
||||
async cancelNotebookByAttachedKernel(viewType: string, uri: URI): Promise<void> {
|
||||
return this._proxy.$cancelNotebookByAttachedKernel(viewType, uri, undefined);
|
||||
}
|
||||
|
||||
async $postMessage(editorId: string, forRendererId: string | undefined, value: any): Promise<boolean> {
|
||||
@@ -543,220 +624,20 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
|
||||
}
|
||||
|
||||
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
controller?.handleEdit(resource, editId, label);
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
|
||||
if (textModel) {
|
||||
textModel.$handleEdit(label, () => {
|
||||
return this._proxy.$undoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty);
|
||||
}, () => {
|
||||
return this._proxy.$redoNotebook(textModel.viewType, textModel.uri, editId, textModel.isDirty);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$onContentChange(resource: UriComponents, viewType: string): void {
|
||||
let controller = this._notebookProviders.get(viewType);
|
||||
controller?.handleNotebookChange(resource);
|
||||
}
|
||||
}
|
||||
|
||||
export class MainThreadNotebookController implements IMainNotebookController {
|
||||
private _mapping: Map<string, MainThreadNotebookDocument> = new Map();
|
||||
static documentHandle: number = 0;
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: ExtHostNotebookShape,
|
||||
private _mainThreadNotebook: MainThreadNotebooks,
|
||||
private _viewType: string,
|
||||
private _supportBackup: boolean,
|
||||
readonly kernel: INotebookKernelInfoDto | undefined,
|
||||
readonly notebookService: INotebookService,
|
||||
readonly _instantiationService: IInstantiationService
|
||||
|
||||
) {
|
||||
}
|
||||
|
||||
async createNotebook(viewType: string, uri: URI, backup: INotebookTextModelBackup | undefined, forceReload: boolean, editorId?: string, backupId?: string): Promise<NotebookTextModel | undefined> {
|
||||
let mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
|
||||
|
||||
if (mainthreadNotebook) {
|
||||
if (forceReload) {
|
||||
const data = await this._proxy.$resolveNotebookData(viewType, uri);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
mainthreadNotebook.textModel.languages = data.languages;
|
||||
mainthreadNotebook.textModel.metadata = data.metadata;
|
||||
await mainthreadNotebook.applyEdit(mainthreadNotebook.textModel.versionId, [
|
||||
{ editType: CellEditType.Delete, count: mainthreadNotebook.textModel.cells.length, index: 0 },
|
||||
{ editType: CellEditType.Insert, index: 0, cells: data.cells }
|
||||
], true, false);
|
||||
}
|
||||
return mainthreadNotebook.textModel;
|
||||
}
|
||||
|
||||
let document = this._instantiationService.createInstance(MainThreadNotebookDocument, this._proxy, MainThreadNotebookController.documentHandle++, viewType, this._supportBackup, uri);
|
||||
this._mapping.set(document.uri.toString(), document);
|
||||
|
||||
if (backup) {
|
||||
// trigger events
|
||||
document.textModel.metadata = backup.metadata;
|
||||
document.textModel.languages = backup.languages;
|
||||
|
||||
// restored from backup, update the text model without emitting any event to exthost
|
||||
await document.applyEdit(document.textModel.versionId, [
|
||||
{
|
||||
editType: CellEditType.Insert,
|
||||
index: 0,
|
||||
cells: backup.cells || []
|
||||
}
|
||||
], false, true);
|
||||
|
||||
// create document in ext host with cells data
|
||||
await this._mainThreadNotebook.addNotebookDocument({
|
||||
viewType: document.viewType,
|
||||
handle: document.handle,
|
||||
uri: document.uri,
|
||||
metadata: document.textModel.metadata,
|
||||
versionId: document.textModel.versionId,
|
||||
cells: document.textModel.cells.map(cell => ({
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
})),
|
||||
attachedEditor: editorId ? {
|
||||
id: editorId,
|
||||
selections: document.textModel.selections
|
||||
} : undefined
|
||||
});
|
||||
|
||||
return document.textModel;
|
||||
}
|
||||
|
||||
// open notebook document
|
||||
const data = await this._proxy.$resolveNotebookData(viewType, uri, backupId);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.textModel.languages = data.languages;
|
||||
document.textModel.metadata = data.metadata;
|
||||
|
||||
if (data.cells.length) {
|
||||
document.textModel.initialize(data!.cells);
|
||||
} else {
|
||||
const mainCell = document.textModel.createCellTextModel([''], document.textModel.languages.length ? document.textModel.languages[0] : '', CellKind.Code, [], undefined);
|
||||
document.textModel.insertTemplateCell(mainCell);
|
||||
}
|
||||
|
||||
await this._mainThreadNotebook.addNotebookDocument({
|
||||
viewType: document.viewType,
|
||||
handle: document.handle,
|
||||
uri: document.uri,
|
||||
metadata: document.textModel.metadata,
|
||||
versionId: document.textModel.versionId,
|
||||
cells: document.textModel.cells.map(cell => ({
|
||||
handle: cell.handle,
|
||||
uri: cell.uri,
|
||||
source: cell.textBuffer.getLinesContent(),
|
||||
eol: cell.textBuffer.getEOL(),
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: cell.outputs,
|
||||
metadata: cell.metadata
|
||||
})),
|
||||
attachedEditor: editorId ? {
|
||||
id: editorId,
|
||||
selections: document.textModel.selections
|
||||
} : undefined
|
||||
});
|
||||
|
||||
this._proxy.$acceptEditorPropertiesChanged(uri, { selections: null, metadata: document.textModel.metadata });
|
||||
|
||||
return document.textModel;
|
||||
}
|
||||
|
||||
async resolveNotebookEditor(viewType: string, uri: URI, editorId: string) {
|
||||
await this._proxy.$resolveNotebookEditor(viewType, uri, editorId);
|
||||
}
|
||||
|
||||
async tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
|
||||
let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
|
||||
|
||||
if (mainthreadNotebook) {
|
||||
return await mainthreadNotebook.applyEdit(modelVersionId, edits, true, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async spliceNotebookCellOutputs(resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
|
||||
let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
|
||||
await mainthreadNotebook?.spliceNotebookCellOutputs(cellHandle, splices);
|
||||
}
|
||||
|
||||
async executeNotebookByAttachedKernel(viewType: string, uri: URI, token: CancellationToken): Promise<void> {
|
||||
return this._mainThreadNotebook.executeNotebookByAttachedKernel(viewType, uri, token);
|
||||
}
|
||||
|
||||
onDidReceiveMessage(editorId: string, rendererType: string | undefined, message: unknown): void {
|
||||
this._proxy.$onDidReceiveMessage(editorId, rendererType, message);
|
||||
}
|
||||
|
||||
async removeNotebookDocument(notebook: INotebookTextModel): Promise<void> {
|
||||
let document = this._mapping.get(URI.from(notebook.uri).toString());
|
||||
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO@rebornix, remove cell should use emitDelta as well to ensure document/editor events are sent together
|
||||
await this._proxy.$acceptDocumentAndEditorsDelta({ removedDocuments: [notebook.uri] });
|
||||
document.dispose();
|
||||
this._mapping.delete(URI.from(notebook.uri).toString());
|
||||
}
|
||||
|
||||
// Methods for ExtHost
|
||||
|
||||
handleNotebookChange(resource: UriComponents) {
|
||||
let document = this._mapping.get(URI.from(resource).toString());
|
||||
document?.textModel.handleUnknownChange();
|
||||
}
|
||||
|
||||
handleEdit(resource: UriComponents, editId: number, label: string | undefined): void {
|
||||
let document = this._mapping.get(URI.from(resource).toString());
|
||||
document?.handleEdit(editId, label);
|
||||
}
|
||||
|
||||
updateLanguages(resource: UriComponents, languages: string[]) {
|
||||
let document = this._mapping.get(URI.from(resource).toString());
|
||||
document?.textModel.updateLanguages(languages);
|
||||
}
|
||||
|
||||
updateNotebookMetadata(resource: UriComponents, metadata: NotebookDocumentMetadata) {
|
||||
let document = this._mapping.get(URI.from(resource).toString());
|
||||
document?.textModel.updateNotebookMetadata(metadata);
|
||||
}
|
||||
|
||||
updateNotebookCellMetadata(resource: UriComponents, handle: number, metadata: NotebookCellMetadata) {
|
||||
let document = this._mapping.get(URI.from(resource).toString());
|
||||
document?.textModel.updateNotebookCellMetadata(handle, metadata);
|
||||
}
|
||||
|
||||
async executeNotebookCell(uri: URI, handle: number, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$executeNotebookByAttachedKernel(this._viewType, uri, handle, token);
|
||||
}
|
||||
|
||||
async save(uri: URI, token: CancellationToken): Promise<boolean> {
|
||||
return this._proxy.$saveNotebook(this._viewType, uri, token);
|
||||
}
|
||||
|
||||
async saveAs(uri: URI, target: URI, token: CancellationToken): Promise<boolean> {
|
||||
return this._proxy.$saveNotebookAs(this._viewType, uri, target, token);
|
||||
}
|
||||
|
||||
async backup(uri: URI, token: CancellationToken): Promise<string | undefined> {
|
||||
const backupId = await this._proxy.$backup(this._viewType, uri, token);
|
||||
return backupId;
|
||||
const textModel = this._notebookService.getNotebookTextModel(URI.from(resource));
|
||||
textModel?.handleUnknownChange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -772,8 +653,8 @@ export class MainThreadNotebookKernel implements INotebookKernelInfo {
|
||||
) {
|
||||
}
|
||||
|
||||
async executeNotebook(viewType: string, uri: URI, handle: number | undefined, token: CancellationToken): Promise<void> {
|
||||
return this._proxy.$executeNotebook2(this.id, viewType, uri, handle, token);
|
||||
async executeNotebook(viewType: string, uri: URI, handle: number | undefined): Promise<void> {
|
||||
return this._proxy.$executeNotebook2(this.id, viewType, uri, handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,9 +66,10 @@ class MainThreadSCMResource implements ISCMResource {
|
||||
private readonly sourceControlHandle: number,
|
||||
private readonly groupHandle: number,
|
||||
private readonly handle: number,
|
||||
public sourceUri: URI,
|
||||
public resourceGroup: ISCMResourceGroup,
|
||||
public decorations: ISCMResourceDecorations
|
||||
readonly sourceUri: URI,
|
||||
readonly resourceGroup: ISCMResourceGroup,
|
||||
readonly decorations: ISCMResourceDecorations,
|
||||
readonly contextValue: string | undefined
|
||||
) { }
|
||||
|
||||
open(preserveFocus: boolean): Promise<void> {
|
||||
@@ -150,18 +151,22 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
}
|
||||
}
|
||||
|
||||
$registerGroup(handle: number, id: string, label: string): void {
|
||||
const group = new MainThreadSCMResourceGroup(
|
||||
this.handle,
|
||||
handle,
|
||||
this,
|
||||
{},
|
||||
label,
|
||||
id
|
||||
);
|
||||
$registerGroups(_groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][]): void {
|
||||
const groups = _groups.map(([handle, id, label, features]) => {
|
||||
const group = new MainThreadSCMResourceGroup(
|
||||
this.handle,
|
||||
handle,
|
||||
this,
|
||||
features,
|
||||
label,
|
||||
id
|
||||
);
|
||||
|
||||
this._groupsByHandle[handle] = group;
|
||||
this.groups.splice(this.groups.elements.length, 0, [group]);
|
||||
this._groupsByHandle[handle] = group;
|
||||
return group;
|
||||
});
|
||||
|
||||
this.groups.splice(this.groups.elements.length, 0, groups);
|
||||
}
|
||||
|
||||
$updateGroup(handle: number, features: SCMGroupFeatures): void {
|
||||
@@ -198,7 +203,7 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
|
||||
for (const [start, deleteCount, rawResources] of groupSlices) {
|
||||
const resources = rawResources.map(rawResource => {
|
||||
const [handle, sourceUri, icons, tooltip, strikeThrough, faded] = rawResource;
|
||||
const [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] = rawResource;
|
||||
const icon = icons[0];
|
||||
const iconDark = icons[1] || icon;
|
||||
const decorations = {
|
||||
@@ -216,7 +221,8 @@ class MainThreadSCMProvider implements ISCMProvider {
|
||||
handle,
|
||||
URI.revive(sourceUri),
|
||||
group,
|
||||
decorations
|
||||
decorations,
|
||||
contextValue || undefined
|
||||
);
|
||||
});
|
||||
|
||||
@@ -326,7 +332,7 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
||||
this._repositories.delete(handle);
|
||||
}
|
||||
|
||||
$registerGroup(sourceControlHandle: number, groupHandle: number, id: string, label: string): void {
|
||||
$registerGroups(sourceControlHandle: number, groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][], splices: SCMRawResourceSplices[]): void {
|
||||
const repository = this._repositories.get(sourceControlHandle);
|
||||
|
||||
if (!repository) {
|
||||
@@ -334,7 +340,8 @@ export class MainThreadSCM implements MainThreadSCMShape {
|
||||
}
|
||||
|
||||
const provider = repository.provider as MainThreadSCMProvider;
|
||||
provider.$registerGroup(groupHandle, id, label);
|
||||
provider.$registerGroups(groups);
|
||||
provider.$spliceGroupResourceStates(splices);
|
||||
}
|
||||
|
||||
$updateGroup(sourceControlHandle: number, groupHandle: number, features: SCMGroupFeatures): void {
|
||||
|
||||
@@ -613,6 +613,9 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
public $registerTaskSystem(key: string, info: TaskSystemInfoDTO): void {
|
||||
let platform: Platform.Platform;
|
||||
switch (info.platform) {
|
||||
case 'Web':
|
||||
platform = Platform.Platform.Web;
|
||||
break;
|
||||
case 'win32':
|
||||
platform = Platform.Platform.Windows;
|
||||
break;
|
||||
@@ -631,7 +634,7 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
return URI.parse(`${info.scheme}://${info.authority}${path}`);
|
||||
},
|
||||
context: this._extHostContext,
|
||||
resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise<ResolvedVariables> => {
|
||||
resolveVariables: (workspaceFolder: IWorkspaceFolder, toResolve: ResolveSet, target: ConfigurationTarget): Promise<ResolvedVariables | undefined> => {
|
||||
const vars: string[] = [];
|
||||
toResolve.variables.forEach(item => vars.push(item));
|
||||
return Promise.resolve(this._proxy.$resolveVariables(workspaceFolder.uri, { process: toResolve.process, variables: vars })).then(values => {
|
||||
@@ -639,8 +642,12 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
forEach(values.variables, (entry) => {
|
||||
partiallyResolvedVars.push(entry.value);
|
||||
});
|
||||
return new Promise<ResolvedVariables>((resolve, reject) => {
|
||||
return new Promise<ResolvedVariables | undefined>((resolve, reject) => {
|
||||
this._configurationResolverService.resolveWithInteraction(workspaceFolder, partiallyResolvedVars, 'tasks', undefined, target).then(resolvedVars => {
|
||||
if (!resolvedVars) {
|
||||
resolve(undefined);
|
||||
}
|
||||
|
||||
const result: ResolvedVariables = {
|
||||
process: undefined,
|
||||
variables: new Map<string, string>()
|
||||
@@ -674,4 +681,9 @@ export class MainThreadTask implements MainThreadTaskShape {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async $registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean): Promise<void> {
|
||||
return this._taskService.registerSupportedExecutions(custom, shell, process);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
|
||||
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
|
||||
@@ -40,6 +41,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
|
||||
this._remoteAuthority = extHostContext.remoteAuthority;
|
||||
@@ -217,7 +219,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
executable: terminalInstance.shellLaunchConfig.executable,
|
||||
args: terminalInstance.shellLaunchConfig.args,
|
||||
cwd: terminalInstance.shellLaunchConfig.cwd,
|
||||
env: terminalInstance.shellLaunchConfig.env
|
||||
env: terminalInstance.shellLaunchConfig.env,
|
||||
hideFromUser: terminalInstance.shellLaunchConfig.hideFromUser
|
||||
};
|
||||
if (terminalInstance.title) {
|
||||
this._proxy.$acceptTerminalOpened(terminalInstance.id, terminalInstance.title, shellLaunchConfigDto);
|
||||
@@ -259,6 +262,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
|
||||
env: request.shellLaunchConfig.env
|
||||
};
|
||||
|
||||
this._logService.trace('Spawning ext host process', { terminalId: proxy.terminalId, shellLaunchConfigDto, request });
|
||||
this._proxy.$spawnExtHostProcess(
|
||||
proxy.terminalId,
|
||||
shellLaunchConfigDto,
|
||||
|
||||
@@ -5,17 +5,18 @@
|
||||
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable, IReference } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { isEqual, isEqualOrParent } from 'vs/base/common/resources';
|
||||
import { isEqual, isEqualOrParent, toLocalResource } from 'vs/base/common/resources';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
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 { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -38,6 +39,7 @@ import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOption
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
@@ -661,10 +663,12 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
fromBackup: boolean,
|
||||
private readonly _editable: boolean,
|
||||
private readonly _getEditors: () => CustomEditorInput[],
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IFileDialogService private readonly _fileDialogService: IFileDialogService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IUndoRedoService private readonly _undoService: IUndoRedoService,
|
||||
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -845,11 +849,20 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return !!await this.saveCustomEditor(options);
|
||||
}
|
||||
|
||||
public async saveCustomEditor(_options?: ISaveOptions): Promise<URI | undefined> {
|
||||
public async saveCustomEditor(options?: ISaveOptions): Promise<URI | undefined> {
|
||||
if (!this._editable) {
|
||||
return undefined;
|
||||
}
|
||||
// TODO: handle save untitled case
|
||||
|
||||
if (this._editorResource.scheme === Schemas.untitled) {
|
||||
const targetUri = await this.suggestUntitledSavePath(options);
|
||||
if (!targetUri) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
await this.saveCustomEditorAs(this._editorResource, targetUri, options);
|
||||
return targetUri;
|
||||
}
|
||||
|
||||
const savePromise = createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
|
||||
this._ongoingSave?.cancel();
|
||||
@@ -871,6 +884,18 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
return this._editorResource;
|
||||
}
|
||||
|
||||
private suggestUntitledSavePath(options: ISaveOptions | undefined): Promise<URI | undefined> {
|
||||
if (this._editorResource.scheme !== Schemas.untitled) {
|
||||
throw new Error('Resource is not untitled');
|
||||
}
|
||||
|
||||
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
|
||||
const localResrouce = toLocalResource(this._editorResource, remoteAuthority);
|
||||
|
||||
|
||||
return this._fileDialogService.pickFileToSave(localResrouce, options?.availableFileSystems);
|
||||
}
|
||||
|
||||
public async saveCustomEditorAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
|
||||
if (this._editable) {
|
||||
// TODO: handle cancellation
|
||||
|
||||
@@ -127,8 +127,9 @@ const viewDescriptor: IJSONSchema = {
|
||||
'hidden',
|
||||
'collapsed'
|
||||
],
|
||||
default: 'visible',
|
||||
enumDescriptions: [
|
||||
localize('vscode.extension.contributes.view.initialState.visible', "The default initial state for view. The view will be expanded. This may have different behavior when the view container that the view is in is built in."),
|
||||
localize('vscode.extension.contributes.view.initialState.visible', "The default initial state for the view. In most containers the view will be expanded, however; some built-in containers (explorer, scm, and debug) show all contributed views collapsed regardless of the `visibility`."),
|
||||
localize('vscode.extension.contributes.view.initialState.hidden', "The view will not be shown in the view container, but will be discoverable through the views menu and other view entry points and can be un-hidden by the user."),
|
||||
localize('vscode.extension.contributes.view.initialState.collapsed', "The view will show in the view container, but will be collapsed.")
|
||||
]
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, IDefaultConfigurationExtension, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IConfigurationNode, IConfigurationRegistry, Extensions, resourceLanguageSettingsSchemaId, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
@@ -105,20 +105,11 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint<I
|
||||
});
|
||||
defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => {
|
||||
if (removed.length) {
|
||||
const removedDefaultConfigurations: IDefaultConfigurationExtension[] = removed.map(extension => {
|
||||
const id = extension.description.identifier;
|
||||
const name = extension.description.name;
|
||||
const defaults = objects.deepClone(extension.value);
|
||||
return <IDefaultConfigurationExtension>{
|
||||
id, name, defaults
|
||||
};
|
||||
});
|
||||
const removedDefaultConfigurations = removed.map<IStringDictionary<any>>(extension => objects.deepClone(extension.value));
|
||||
configurationRegistry.deregisterDefaultConfigurations(removedDefaultConfigurations);
|
||||
}
|
||||
if (added.length) {
|
||||
const addedDefaultConfigurations = added.map(extension => {
|
||||
const id = extension.description.identifier;
|
||||
const name = extension.description.name;
|
||||
const addedDefaultConfigurations = added.map<IStringDictionary<any>>(extension => {
|
||||
const defaults: IStringDictionary<any> = objects.deepClone(extension.value);
|
||||
for (const key of Object.keys(defaults)) {
|
||||
if (!OVERRIDE_PROPERTY_PATTERN.test(key) || typeof defaults[key] !== 'object') {
|
||||
@@ -126,9 +117,7 @@ defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => {
|
||||
delete defaults[key];
|
||||
}
|
||||
}
|
||||
return <IDefaultConfigurationExtension>{
|
||||
id, name, defaults
|
||||
};
|
||||
return defaults;
|
||||
});
|
||||
configurationRegistry.registerDefaultConfigurations(addedDefaultConfigurations);
|
||||
}
|
||||
|
||||
@@ -205,13 +205,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
get providers(): ReadonlyArray<vscode.AuthenticationProviderInformation> {
|
||||
return extHostAuthentication.providers;
|
||||
},
|
||||
getSession(providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions) {
|
||||
getSession(providerId: string, scopes: string[], options?: vscode.AuthenticationGetSessionOptions) {
|
||||
return extHostAuthentication.getSession(extension, providerId, scopes, options as any);
|
||||
},
|
||||
logout(providerId: string, sessionId: string): Thenable<void> {
|
||||
return extHostAuthentication.logout(providerId, sessionId);
|
||||
},
|
||||
get onDidChangeSessions(): Event<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent> {
|
||||
get onDidChangeSessions(): Event<vscode.AuthenticationSessionsChangeEvent> {
|
||||
return extHostAuthentication.onDidChangeSessions;
|
||||
},
|
||||
};
|
||||
@@ -870,7 +870,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
}
|
||||
return extHostDebugService.startDebugging(folder, nameOrConfig, parentSessionOrOptions || {});
|
||||
},
|
||||
stopDebugging(session: vscode.DebugSession | undefined) {
|
||||
stopDebugging(session?: vscode.DebugSession) {
|
||||
return extHostDebugService.stopDebugging(session);
|
||||
},
|
||||
addBreakpoints(breakpoints: vscode.Breakpoint[]) {
|
||||
@@ -921,6 +921,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidCloseNotebookDocument;
|
||||
},
|
||||
get onDidSaveNotebookDocument(): Event<vscode.NotebookDocument> {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidSaveNotebookDocument;
|
||||
},
|
||||
get notebookDocuments(): vscode.NotebookDocument[] {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.notebookDocuments;
|
||||
@@ -933,10 +937,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeVisibleNotebookEditors;
|
||||
},
|
||||
get activeNotebookKernel() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.activeNotebookKernel;
|
||||
},
|
||||
get onDidChangeActiveNotebookKernel() {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeActiveNotebookKernel;
|
||||
@@ -977,6 +977,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeCellLanguage(listener, thisArgs, disposables);
|
||||
},
|
||||
onDidChangeCellMetadata(listener, thisArgs?, disposables?) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostNotebook.onDidChangeCellMetadata(listener, thisArgs, disposables);
|
||||
},
|
||||
createConcatTextDocument(notebook, selector) {
|
||||
checkProposedApiEnabled(extension);
|
||||
return new ExtHostNotebookConcatDocument(extHostNotebook, extHostDocuments, notebook, selector);
|
||||
@@ -1112,7 +1116,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
TimelineItem: extHostTypes.TimelineItem,
|
||||
CellKind: extHostTypes.CellKind,
|
||||
CellOutputKind: extHostTypes.CellOutputKind,
|
||||
NotebookCellRunState: extHostTypes.NotebookCellRunState
|
||||
NotebookCellRunState: extHostTypes.NotebookCellRunState,
|
||||
NotebookRunState: extHostTypes.NotebookRunState
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -159,13 +159,14 @@ export interface MainThreadCommentsShape extends IDisposable {
|
||||
export interface MainThreadAuthenticationShape extends IDisposable {
|
||||
$registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): void;
|
||||
$unregisterAuthenticationProvider(id: string): void;
|
||||
$ensureProvider(id: string): Promise<void>;
|
||||
$getProviderIds(): Promise<string[]>;
|
||||
$sendDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
|
||||
$getSession(providerId: string, scopes: string[], extensionId: string, extensionName: string, options: { createIfNone?: boolean, clearSessionPreference?: boolean }): Promise<modes.AuthenticationSession | undefined>;
|
||||
$selectSession(providerId: string, providerName: string, extensionId: string, extensionName: string, potentialSessions: modes.AuthenticationSession[], scopes: string[], clearSessionPreference: boolean): Promise<modes.AuthenticationSession>;
|
||||
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerName: string, extensionName: string): Promise<boolean>;
|
||||
$setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void>;
|
||||
$setTrustedExtensionAndAccountPreference(providerId: string, accountName: string, extensionId: string, extensionName: string, sessionId: string): Promise<void>;
|
||||
$requestNewSession(providerId: string, scopes: string[], extensionId: string, extensionName: string): Promise<void>;
|
||||
|
||||
$getSessions(providerId: string): Promise<ReadonlyArray<modes.AuthenticationSession>>;
|
||||
@@ -597,6 +598,7 @@ export interface WebviewExtensionDescription {
|
||||
export interface NotebookExtensionDescription {
|
||||
readonly id: ExtensionIdentifier;
|
||||
readonly location: UriComponents;
|
||||
readonly description?: string;
|
||||
}
|
||||
|
||||
export enum WebviewEditorCapabilities {
|
||||
@@ -786,6 +788,7 @@ export interface MainThreadTaskShape extends IDisposable {
|
||||
$terminateTask(id: string): Promise<void>;
|
||||
$registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void;
|
||||
$customExecutionComplete(id: string, result?: number): Promise<void>;
|
||||
$registerSupportedExecutions(custom?: boolean, shell?: boolean, process?: boolean): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadExtensionServiceShape extends IDisposable {
|
||||
@@ -794,7 +797,7 @@ 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): void;
|
||||
$onExtensionHostExit(code: number): Promise<void>;
|
||||
}
|
||||
|
||||
export interface SCMProviderFeatures {
|
||||
@@ -815,7 +818,8 @@ export type SCMRawResource = [
|
||||
UriComponents[] /*icons: light, dark*/,
|
||||
string /*tooltip*/,
|
||||
boolean /*strike through*/,
|
||||
boolean /*faded*/
|
||||
boolean /*faded*/,
|
||||
string /*context value*/
|
||||
];
|
||||
|
||||
export type SCMRawResourceSplice = [
|
||||
@@ -834,7 +838,7 @@ export interface MainThreadSCMShape extends IDisposable {
|
||||
$updateSourceControl(handle: number, features: SCMProviderFeatures): void;
|
||||
$unregisterSourceControl(handle: number): void;
|
||||
|
||||
$registerGroup(sourceControlHandle: number, handle: number, id: string, label: string): void;
|
||||
$registerGroups(sourceControlHandle: number, groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][], splices: SCMRawResourceSplices[]): void;
|
||||
$updateGroup(sourceControlHandle: number, handle: number, features: SCMGroupFeatures): void;
|
||||
$updateGroupLabel(sourceControlHandle: number, handle: number, label: string): void;
|
||||
$unregisterGroup(sourceControlHandle: number, handle: number): void;
|
||||
@@ -877,7 +881,6 @@ export interface MainThreadDebugServiceShape extends IDisposable {
|
||||
$stopDebugging(sessionId: DebugSessionUUID | undefined): Promise<void>;
|
||||
$setDebugSessionName(id: DebugSessionUUID, name: string): void;
|
||||
$customDebugAdapterRequest(id: DebugSessionUUID, command: string, args: any): Promise<any>;
|
||||
$terminateDebugSession(id: DebugSessionUUID): Promise<void>;
|
||||
$appendDebugConsole(value: string): void;
|
||||
$startBreakpointEvents(): void;
|
||||
$registerBreakpoints(breakpoints: Array<ISourceMultiBreakpointDto | IFunctionBreakpointDto | IDataBreakpointDto>): Promise<void>;
|
||||
@@ -1382,6 +1385,7 @@ export interface IShellLaunchConfigDto {
|
||||
args?: string[] | string;
|
||||
cwd?: string | UriComponents;
|
||||
env?: { [key: string]: string | null; };
|
||||
hideFromUser?: boolean;
|
||||
}
|
||||
|
||||
export interface IShellDefinitionDto {
|
||||
@@ -1616,17 +1620,21 @@ export interface ExtHostNotebookShape {
|
||||
$resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<void>;
|
||||
$provideNotebookKernels(handle: number, uri: UriComponents, token: CancellationToken): Promise<INotebookKernelInfoDto2[]>;
|
||||
$resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void>;
|
||||
$executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise<void>;
|
||||
$executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined, token: CancellationToken): Promise<void>;
|
||||
$executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise<void>;
|
||||
$executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void>;
|
||||
$cancelNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): 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>;
|
||||
$executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void>;
|
||||
$saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean>;
|
||||
$backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined>;
|
||||
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void;
|
||||
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined }): void;
|
||||
$renderOutputs(uriComponents: UriComponents, id: string, request: IOutputRenderRequest<UriComponents>): Promise<IOutputRenderResponse<UriComponents> | undefined>;
|
||||
$renderOutputs2<T>(uriComponents: UriComponents, id: string, request: IOutputRenderRequest<T>): Promise<IOutputRenderResponse<T> | undefined>;
|
||||
$onDidReceiveMessage(editorId: string, rendererId: string | undefined, message: unknown): void;
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void;
|
||||
$acceptModelSaved(uriComponents: UriComponents): void;
|
||||
$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void;
|
||||
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): Promise<void>;
|
||||
$undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
|
||||
|
||||
@@ -21,8 +21,8 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
private _onDidChangeAuthenticationProviders = new Emitter<vscode.AuthenticationProvidersChangeEvent>();
|
||||
readonly onDidChangeAuthenticationProviders: Event<vscode.AuthenticationProvidersChangeEvent> = this._onDidChangeAuthenticationProviders.event;
|
||||
|
||||
private _onDidChangeSessions = new Emitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
|
||||
readonly onDidChangeSessions: Event<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent> = this._onDidChangeSessions.event;
|
||||
private _onDidChangeSessions = new Emitter<vscode.AuthenticationSessionsChangeEvent>();
|
||||
readonly onDidChangeSessions: Event<vscode.AuthenticationSessionsChangeEvent> = this._onDidChangeSessions.event;
|
||||
|
||||
constructor(mainContext: IMainContext) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication);
|
||||
@@ -37,11 +37,12 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
}
|
||||
|
||||
get providers(): ReadonlyArray<vscode.AuthenticationProviderInformation> {
|
||||
return Object.freeze(this._providers);
|
||||
return Object.freeze(this._providers.slice());
|
||||
}
|
||||
|
||||
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions & { createIfNone: true }): Promise<vscode.AuthenticationSession>;
|
||||
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions): Promise<vscode.AuthenticationSession | undefined> {
|
||||
async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: string[], options: vscode.AuthenticationGetSessionOptions = {}): Promise<vscode.AuthenticationSession | undefined> {
|
||||
await this._proxy.$ensureProvider(providerId);
|
||||
const provider = this._authenticationProviders.get(providerId);
|
||||
const extensionName = requestingExtension.displayName || requestingExtension.name;
|
||||
const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier);
|
||||
@@ -75,7 +76,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
}
|
||||
|
||||
const session = await provider.login(scopes);
|
||||
await this._proxy.$setTrustedExtension(providerId, session.account.label, extensionId, extensionName);
|
||||
await this._proxy.$setTrustedExtensionAndAccountPreference(providerId, session.account.label, extensionId, extensionName, session.id);
|
||||
return session;
|
||||
} else {
|
||||
await this._proxy.$requestNewSession(providerId, scopes, extensionId, extensionName);
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape {
|
||||
addBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
|
||||
removeBreakpoints(breakpoints0: vscode.Breakpoint[]): Promise<void>;
|
||||
startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean>;
|
||||
stopDebugging(session: vscode.DebugSession | undefined): Promise<void>;
|
||||
stopDebugging(session?: vscode.DebugSession): Promise<void>;
|
||||
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider, trigger: vscode.DebugConfigurationProviderTriggerKind): vscode.Disposable;
|
||||
registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable;
|
||||
registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable;
|
||||
@@ -302,7 +302,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E
|
||||
});
|
||||
}
|
||||
|
||||
public stopDebugging(session: vscode.DebugSession | undefined): Promise<void> {
|
||||
public stopDebugging(session?: vscode.DebugSession): Promise<void> {
|
||||
return this._debugServiceProxy.$stopDebugging(session ? session.id : undefined);
|
||||
}
|
||||
|
||||
@@ -957,10 +957,6 @@ export class ExtHostDebugSession implements vscode.DebugSession {
|
||||
public customRequest(command: string, args: any): Promise<any> {
|
||||
return this._debugServiceProxy.$customDebugAdapterRequest(this._id, command, args);
|
||||
}
|
||||
|
||||
public terminate(): Promise<void> {
|
||||
return this._debugServiceProxy.$terminateDebugSession(this._id);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostDebugConsole implements vscode.DebugConsole {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { originalFSPath, joinPath } from 'vs/base/common/resources';
|
||||
import { Barrier, timeout } from 'vs/base/common/async';
|
||||
import { dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -193,8 +194,6 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
|
||||
|
||||
public async deactivateAll(): Promise<void> {
|
||||
let allPromises: Promise<void>[] = [];
|
||||
try {
|
||||
@@ -254,7 +253,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
if (!this._extensionPathIndex) {
|
||||
const tree = TernarySearchTree.forPaths<IExtensionDescription>();
|
||||
const extensions = this._registry.getAllExtensionDescriptions().map(ext => {
|
||||
if (!ext.main) {
|
||||
if (!this._getEntryPoint(ext)) {
|
||||
return undefined;
|
||||
}
|
||||
return this._hostUtils.realpath(ext.extensionLocation.fsPath).then(value => tree.set(URI.file(value).fsPath, ext));
|
||||
@@ -345,7 +344,8 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
const event = getTelemetryActivationEvent(extensionDescription, reason);
|
||||
type ActivatePluginClassification = {} & TelemetryActivationEventFragment;
|
||||
this._mainThreadTelemetryProxy.$publicLog2<TelemetryActivationEvent, ActivatePluginClassification>('activatePlugin', event);
|
||||
if (!extensionDescription.main) {
|
||||
const entryPoint = this._getEntryPoint(extensionDescription);
|
||||
if (!entryPoint) {
|
||||
// Treat the extension as being empty => NOT AN ERROR CASE
|
||||
return Promise.resolve(new EmptyExtension(ExtensionActivationTimes.NONE));
|
||||
}
|
||||
@@ -355,15 +355,13 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
|
||||
const activationTimesBuilder = new ExtensionActivationTimesBuilder(reason.startup);
|
||||
return Promise.all([
|
||||
this._loadCommonJSModule<IExtensionModule>(joinPath(extensionDescription.extensionLocation, extensionDescription.main), activationTimesBuilder),
|
||||
this._loadCommonJSModule<IExtensionModule>(joinPath(extensionDescription.extensionLocation, entryPoint), activationTimesBuilder),
|
||||
this._loadExtensionContext(extensionDescription)
|
||||
]).then(values => {
|
||||
return AbstractExtHostExtensionService._callActivate(this._logService, extensionDescription.identifier, values[0], values[1], activationTimesBuilder);
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
|
||||
|
||||
private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise<vscode.ExtensionContext> {
|
||||
|
||||
const globalState = new ExtensionMemento(extensionDescription.identifier.value, true, this._storage);
|
||||
@@ -386,7 +384,14 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
subscriptions: [],
|
||||
get extensionUri() { return extensionDescription.extensionLocation; },
|
||||
get extensionPath() { return extensionDescription.extensionLocation.fsPath; },
|
||||
asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
|
||||
asAbsolutePath(relativePath: string) {
|
||||
if (platform.isWeb) {
|
||||
// web worker
|
||||
return URI.joinPath(extensionDescription.extensionLocation, relativePath).toString();
|
||||
} else {
|
||||
return path.join(extensionDescription.extensionLocation.fsPath, relativePath);
|
||||
}
|
||||
},
|
||||
get storagePath() { return that._storagePath.workspaceValue(extensionDescription)?.fsPath; },
|
||||
get globalStoragePath() { return that._storagePath.globalValue(extensionDescription).fsPath; },
|
||||
get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); },
|
||||
@@ -557,7 +562,7 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
}
|
||||
|
||||
// after tests have run, we shutdown the host
|
||||
this._gracefulExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
|
||||
this._testRunnerExit(error || (typeof failures === 'number' && failures > 0) ? 1 /* ERROR */ : 0 /* OK */);
|
||||
};
|
||||
|
||||
const runResult = testRunner!.run(extensionTestsPath, oldTestRunnerCallback);
|
||||
@@ -567,11 +572,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
runResult
|
||||
.then(() => {
|
||||
c();
|
||||
this._gracefulExit(0);
|
||||
this._testRunnerExit(0);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
e(err.toString());
|
||||
this._gracefulExit(1);
|
||||
this._testRunnerExit(1);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -579,24 +584,20 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
|
||||
// Otherwise make sure to shutdown anyway even in case of an error
|
||||
else {
|
||||
this._gracefulExit(1 /* ERROR */);
|
||||
this._testRunnerExit(1 /* ERROR */);
|
||||
}
|
||||
|
||||
return Promise.reject(new Error(requireError ? requireError.toString() : nls.localize('extensionTestError', "Path {0} does not point to a valid extension test runner.", extensionTestsPath)));
|
||||
}
|
||||
|
||||
private _gracefulExit(code: number): void {
|
||||
// to give the PH process a chance to flush any outstanding console
|
||||
// messages to the main process, we delay the exit() by some time
|
||||
setTimeout(() => {
|
||||
// If extension tests are running, give the exit code to the renderer
|
||||
if (this._initData.remote.isRemote && !!this._initData.environment.extensionTestsLocationURI) {
|
||||
this._mainThreadExtensionsProxy.$onExtensionHostExit(code);
|
||||
return;
|
||||
}
|
||||
|
||||
private _testRunnerExit(code: number): void {
|
||||
// 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._hostUtils.exit(code);
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
private _startExtensionHost(): Promise<void> {
|
||||
@@ -751,6 +752,9 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme
|
||||
this._onDidChangeRemoteConnectionData.fire();
|
||||
}
|
||||
|
||||
protected abstract _beforeAlmostReadyToRunExtensions(): Promise<void>;
|
||||
protected abstract _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined;
|
||||
protected abstract _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T>;
|
||||
public abstract async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,28 +3,29 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { readonly } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
import { Disposable, DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
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 { CellKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta, IModelAddedData } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CellKind, ExtHostNotebookShape, IMainContext, IModelAddedData, INotebookDocumentsAndEditorsDelta, INotebookEditorPropertiesChangeData, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { CellEditType, diff, ICellEditOperation, ICellInsertEdit, INotebookDisplayOrder, INotebookEditData, NotebookCellsChangedEvent, NotebookCellsSplice2, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseOutputInfo, IOutputRenderResponseCellInfo, IRawOutput, CellOutputKind, IProcessedOutput, INotebookKernelInfoDto2, IMainCellDto } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { hash } from 'vs/base/common/hash';
|
||||
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 { CellEditType, CellOutputKind, diff, ICellDeleteEdit, ICellEditOperation, ICellInsertEdit, IMainCellDto, INotebookDisplayOrder, INotebookEditData, INotebookKernelInfoDto2, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseCellInfo, IOutputRenderResponseOutputInfo, IProcessedOutput, IRawOutput, NotebookCellMetadata, NotebookCellsChangedEvent, NotebookCellsChangeType, NotebookCellsSplice2, NotebookDataDto, notebookDocumentMetadataDefaults } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
|
||||
|
||||
interface IObservable<T> {
|
||||
proxy: T;
|
||||
onDidChange: Event<void>;
|
||||
@@ -50,6 +51,7 @@ interface INotebookEventEmitter {
|
||||
emitModelChange(events: vscode.NotebookCellsChangeEvent): void;
|
||||
emitCellOutputsChange(event: vscode.NotebookCellOutputsChangeEvent): void;
|
||||
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void;
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void;
|
||||
}
|
||||
|
||||
const addIdToOutput = (output: IRawOutput, id = UUID.generateUuid()): IProcessedOutput => output.outputKind === CellOutputKind.Rich
|
||||
@@ -119,7 +121,7 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
}
|
||||
|
||||
set outputs(newOutputs: vscode.CellOutput[]) {
|
||||
let rawDiffs = diff<vscode.CellOutput>(this._outputs || [], newOutputs || [], (a) => {
|
||||
const rawDiffs = diff<vscode.CellOutput>(this._outputs || [], newOutputs || [], (a) => {
|
||||
return this._outputMapping.has(a);
|
||||
});
|
||||
|
||||
@@ -153,6 +155,11 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
}
|
||||
|
||||
set metadata(newMetadata: vscode.NotebookCellMetadata) {
|
||||
this.setMetadata(newMetadata);
|
||||
this._updateMetadata();
|
||||
}
|
||||
|
||||
setMetadata(newMetadata: vscode.NotebookCellMetadata): void {
|
||||
// Don't apply metadata defaults here, 'undefined' means 'inherit from document metadata'
|
||||
this._metadataChangeListener.dispose();
|
||||
const observableMetadata = getObservable(newMetadata);
|
||||
@@ -160,8 +167,6 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => {
|
||||
this._updateMetadata();
|
||||
}));
|
||||
|
||||
this._updateMetadata();
|
||||
}
|
||||
|
||||
private _updateMetadata(): Promise<void> {
|
||||
@@ -192,6 +197,10 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
this._proxy.$updateNotebookLanguages(this.viewType, this.uri, this._languages);
|
||||
}
|
||||
|
||||
get isUntitled() {
|
||||
return this.uri.scheme === Schemas.untitled;
|
||||
}
|
||||
|
||||
private _metadata: Required<vscode.NotebookDocumentMetadata> = notebookDocumentMetadataDefaults;
|
||||
private _metadataChangeListener: IDisposable;
|
||||
|
||||
@@ -327,7 +336,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
|
||||
get isDirty() { return false; }
|
||||
|
||||
accpetModelChanged(event: NotebookCellsChangedEvent): void {
|
||||
acceptModelChanged(event: NotebookCellsChangedEvent): void {
|
||||
this._versionId = event.versionId;
|
||||
if (event.kind === NotebookCellsChangeType.Initialize) {
|
||||
this.$spliceNotebookCells(event.changes, true);
|
||||
@@ -341,6 +350,8 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
this.$clearAllCellOutputs();
|
||||
} else if (event.kind === NotebookCellsChangeType.ChangeLanguage) {
|
||||
this.$changeCellLanguage(event.index, event.language);
|
||||
} else if (event.kind === NotebookCellsChangeType.ChangeMetadata) {
|
||||
this.$changeCellMetadata(event.index, event.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,8 +364,8 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
const addedCellDocuments: IModelAddedData[] = [];
|
||||
|
||||
splices.reverse().forEach(splice => {
|
||||
let cellDtos = splice[2];
|
||||
let newCells = cellDtos.map(cell => {
|
||||
const cellDtos = splice[2];
|
||||
const newCells = cellDtos.map(cell => {
|
||||
|
||||
const extCell = new ExtHostCell(this._proxy, this, this._documentsAndEditors, cell);
|
||||
|
||||
@@ -366,7 +377,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
this._cellDisposableMapping.set(extCell.handle, new DisposableStore());
|
||||
}
|
||||
|
||||
let store = this._cellDisposableMapping.get(extCell.handle)!;
|
||||
const store = this._cellDisposableMapping.get(extCell.handle)!;
|
||||
|
||||
store.add(extCell.onDidChangeOutputs((diffs) => {
|
||||
this.eventuallyUpdateCellOutputs(extCell, diffs);
|
||||
@@ -447,10 +458,17 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
this._emitter.emitCellLanguageChange(event);
|
||||
}
|
||||
|
||||
private $changeCellMetadata(index: number, newMetadata: NotebookCellMetadata): void {
|
||||
const cell = this.cells[index];
|
||||
cell.setMetadata(newMetadata);
|
||||
const event: vscode.NotebookCellMetadataChangeEvent = { document: this, cell };
|
||||
this._emitter.emitCellMetadataChange(event);
|
||||
}
|
||||
|
||||
async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<IProcessedOutput>[]) {
|
||||
let renderers = new Set<number>();
|
||||
let outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
|
||||
let outputs = diff.toInsert;
|
||||
const renderers = new Set<number>();
|
||||
const outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
|
||||
const outputs = diff.toInsert;
|
||||
return [diff.start, diff.deleteCount, outputs];
|
||||
});
|
||||
|
||||
@@ -501,7 +519,7 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE
|
||||
this._throwIfFinalized();
|
||||
|
||||
const sourceArr = Array.isArray(content) ? content : content.split(/\r|\n|\r\n/g);
|
||||
let cell = {
|
||||
const cell = {
|
||||
source: sourceArr,
|
||||
language,
|
||||
cellKind: type,
|
||||
@@ -603,6 +621,16 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
this._active = value;
|
||||
}
|
||||
|
||||
private _kernel?: vscode.NotebookKernel;
|
||||
|
||||
get kernel() {
|
||||
return this._kernel;
|
||||
}
|
||||
|
||||
set kernel(_kernel: vscode.NotebookKernel | undefined) {
|
||||
throw readonly('kernel');
|
||||
}
|
||||
|
||||
private _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose: Event<void> = this._onDidDispose.event;
|
||||
private _onDidReceiveMessage = new Emitter<any>();
|
||||
@@ -636,7 +664,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
let compressedEdits: ICellEditOperation[] = [];
|
||||
const compressedEdits: ICellEditOperation[] = [];
|
||||
let compressedEditsIndex = -1;
|
||||
|
||||
for (let i = 0; i < editData.edits.length; i++) {
|
||||
@@ -646,8 +674,8 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
continue;
|
||||
}
|
||||
|
||||
let prevIndex = compressedEditsIndex;
|
||||
let prev = compressedEdits[prevIndex];
|
||||
const prevIndex = compressedEditsIndex;
|
||||
const prev = compressedEdits[prevIndex];
|
||||
|
||||
if (prev.editType === CellEditType.Insert && editData.edits[i].editType === CellEditType.Insert) {
|
||||
if (prev.index === editData.edits[i].index) {
|
||||
@@ -678,6 +706,9 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
throw readonly('viewColumn');
|
||||
}
|
||||
|
||||
updateActiveKernel(kernel?: vscode.NotebookKernel) {
|
||||
this._kernel = kernel;
|
||||
}
|
||||
async postMessage(message: any): Promise<boolean> {
|
||||
return this._webComm.postMessage(message);
|
||||
}
|
||||
@@ -721,7 +752,7 @@ export class ExtHostNotebookOutputRenderer {
|
||||
}
|
||||
|
||||
render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, outputId: string, mimeType: string): string {
|
||||
let html = this.renderer.render(document, { output, outputId, mimeType });
|
||||
const html = this.renderer.render(document, { output, outputId, mimeType });
|
||||
|
||||
return html;
|
||||
}
|
||||
@@ -753,11 +784,18 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
const data = await this._provider.provideKernels(document, token) || [];
|
||||
|
||||
const newMap = new Map<vscode.NotebookKernel, string>();
|
||||
let kernel_unique_pool = 0;
|
||||
const kernelIdCache = new Set<string>();
|
||||
|
||||
const transformedData: INotebookKernelInfoDto2[] = data.map(kernel => {
|
||||
let id = this._kernelToId.get(kernel);
|
||||
if (id === undefined) {
|
||||
id = UUID.generateUuid();
|
||||
if (kernel.id && kernelIdCache.has(kernel.id)) {
|
||||
id = `${this._extension.identifier.value}_${kernel.id}_${kernel_unique_pool++}`;
|
||||
} else {
|
||||
id = `${this._extension.identifier.value}_${kernel.id || UUID.generateUuid()}`;
|
||||
}
|
||||
|
||||
this._kernelToId.set(kernel, id);
|
||||
}
|
||||
|
||||
@@ -784,6 +822,10 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
return transformedData;
|
||||
}
|
||||
|
||||
getKernel(kernelId: string) {
|
||||
return this._idToKernel.get(kernelId);
|
||||
}
|
||||
|
||||
async resolveNotebook(kernelId: string, document: ExtHostNotebookDocument, webview: vscode.NotebookCommunication, token: CancellationToken) {
|
||||
const kernel = this._idToKernel.get(kernelId);
|
||||
|
||||
@@ -792,7 +834,7 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined, token: CancellationToken) {
|
||||
async executeNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
|
||||
const kernel = this._idToKernel.get(kernelId);
|
||||
|
||||
if (!kernel) {
|
||||
@@ -800,11 +842,35 @@ export class ExtHostNotebookKernelProviderAdapter extends Disposable {
|
||||
}
|
||||
|
||||
if (cell) {
|
||||
return kernel.executeCell(document, cell, token);
|
||||
return withToken(token => (kernel.executeCell as any)(document, cell, token));
|
||||
} else {
|
||||
return kernel.executeAllCells(document, token);
|
||||
return withToken(token => (kernel.executeAllCells as any)(document, token));
|
||||
}
|
||||
}
|
||||
|
||||
async cancelNotebook(kernelId: string, document: ExtHostNotebookDocument, cell: ExtHostCell | undefined) {
|
||||
const kernel = this._idToKernel.get(kernelId);
|
||||
|
||||
if (!kernel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell) {
|
||||
return kernel.cancelCellExecution(document, cell);
|
||||
} else {
|
||||
return kernel.cancelAllCellsExecution(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostNotebookOutputRenderingHandler {
|
||||
@@ -816,7 +882,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
private readonly _notebookKernelProviders = new Map<number, ExtHostNotebookKernelProviderAdapter>();
|
||||
private readonly _documents = new Map<string, ExtHostNotebookDocument>();
|
||||
private readonly _unInitializedDocuments = new Map<string, ExtHostNotebookDocument>();
|
||||
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor }>();
|
||||
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor; }>();
|
||||
private readonly _webviewComm = new Map<string, ExtHostWebviewCommWrapper>();
|
||||
private readonly _notebookOutputRenderers = new Map<string, ExtHostNotebookOutputRenderer>();
|
||||
private readonly _renderersUsedInNotebooks = new WeakMap<ExtHostNotebookDocument, Set<ExtHostNotebookOutputRenderer>>();
|
||||
@@ -826,6 +892,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
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;
|
||||
|
||||
@@ -849,10 +917,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
onDidOpenNotebookDocument: Event<vscode.NotebookDocument> = this._onDidOpenNotebookDocument.event;
|
||||
private _onDidCloseNotebookDocument = new Emitter<vscode.NotebookDocument>();
|
||||
onDidCloseNotebookDocument: Event<vscode.NotebookDocument> = this._onDidCloseNotebookDocument.event;
|
||||
private _onDidSaveNotebookDocument = new Emitter<vscode.NotebookDocument>();
|
||||
onDidSaveNotebookDocument: Event<vscode.NotebookDocument> = this._onDidCloseNotebookDocument.event;
|
||||
visibleNotebookEditors: ExtHostNotebookEditor[] = [];
|
||||
activeNotebookKernel?: vscode.NotebookKernel;
|
||||
|
||||
private _onDidChangeActiveNotebookKernel = new Emitter<void>();
|
||||
private _onDidChangeActiveNotebookKernel = new Emitter<{ document: ExtHostNotebookDocument, kernel: vscode.NotebookKernel | undefined; }>();
|
||||
onDidChangeActiveNotebookKernel = this._onDidChangeActiveNotebookKernel.event;
|
||||
private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
|
||||
onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event;
|
||||
@@ -872,7 +940,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
const documentHandle = arg.notebookEditor?.notebookHandle;
|
||||
const cellHandle = arg.cell.handle;
|
||||
|
||||
for (let value of this._editors) {
|
||||
for (const value of this._editors) {
|
||||
if (value[1].editor.document.handle === documentHandle) {
|
||||
const cell = value[1].editor.document.getCell(cellHandle);
|
||||
if (cell) {
|
||||
@@ -896,9 +964,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
throw new Error(`Notebook renderer for '${type}' already registered`);
|
||||
}
|
||||
|
||||
let extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer);
|
||||
const extHostRenderer = new ExtHostNotebookOutputRenderer(type, filter, renderer);
|
||||
this._notebookOutputRenderers.set(extHostRenderer.type, extHostRenderer);
|
||||
this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation }, type, filter, renderer.preloads || []);
|
||||
this._proxy.$registerNotebookRenderer({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, type, filter, renderer.preloads || []);
|
||||
return new extHostTypes.Disposable(() => {
|
||||
this._notebookOutputRenderers.delete(extHostRenderer.type);
|
||||
this._proxy.$unregisterNotebookRenderer(extHostRenderer.type);
|
||||
@@ -978,8 +1046,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
|
||||
findBestMatchedRenderer(mimeType: string): ExtHostNotebookOutputRenderer[] {
|
||||
let matches: ExtHostNotebookOutputRenderer[] = [];
|
||||
for (let renderer of this._notebookOutputRenderers) {
|
||||
const matches: ExtHostNotebookOutputRenderer[] = [];
|
||||
for (const renderer of this._notebookOutputRenderers) {
|
||||
if (renderer[1].matches(mimeType)) {
|
||||
matches.push(renderer[1]);
|
||||
}
|
||||
@@ -1023,7 +1091,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
|
||||
const supportBackup = !!provider.backupNotebook;
|
||||
|
||||
this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined);
|
||||
this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, viewType, supportBackup, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined);
|
||||
|
||||
return new extHostTypes.Disposable(() => {
|
||||
listener.dispose();
|
||||
@@ -1036,7 +1104,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
const handle = ExtHostNotebookController._notebookKernelProviderHandlePool++;
|
||||
const adapter = new ExtHostNotebookKernelProviderAdapter(this._proxy, handle, extension, provider);
|
||||
this._notebookKernelProviders.set(handle, adapter);
|
||||
this._proxy.$registerNotebookKernelProvider({ id: extension.identifier, location: extension.extensionLocation }, handle, {
|
||||
this._proxy.$registerNotebookKernelProvider({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, handle, {
|
||||
viewType: selector.viewType,
|
||||
filenamePattern: selector.filenamePattern ? typeConverters.GlobPattern.from(selector.filenamePattern) : undefined,
|
||||
excludeFileNamePattern: selector.excludeFileNamePattern ? typeConverters.GlobPattern.from(selector.excludeFileNamePattern) : undefined,
|
||||
@@ -1073,7 +1141,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
|
||||
async $resolveNotebookKernel(handle: number, editorId: string, uri: UriComponents, kernelId: string, token: CancellationToken): Promise<void> {
|
||||
await this._withAdapter<void>(handle, uri, async (adapter, document) => {
|
||||
let webComm = this._webviewComm.get(editorId);
|
||||
const webComm = this._webviewComm.get(editorId);
|
||||
|
||||
if (webComm) {
|
||||
await adapter.resolveNotebook(kernelId, document, webComm.contentProviderComm, token);
|
||||
@@ -1089,7 +1157,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
this._notebookKernels.set(id, { kernel, extension });
|
||||
const transformedSelectors = selectors.map(selector => typeConverters.GlobPattern.from(selector));
|
||||
|
||||
this._proxy.$registerNotebookKernel({ id: extension.identifier, location: extension.extensionLocation }, id, kernel.label, transformedSelectors, kernel.preloads || []);
|
||||
this._proxy.$registerNotebookKernel({ id: extension.identifier, location: extension.extensionLocation, description: extension.description }, id, kernel.label, transformedSelectors, kernel.preloads || []);
|
||||
return new extHostTypes.Disposable(() => {
|
||||
this._notebookKernels.delete(id);
|
||||
this._proxy.$unregisterNotebookKernel(id);
|
||||
@@ -1119,7 +1187,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
},
|
||||
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
|
||||
that._onDidChangeCellLanguage.fire(event);
|
||||
}
|
||||
},
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
|
||||
that._onDidChangeCellMetadata.fire(event);
|
||||
},
|
||||
}, viewType, revivedUri, this, storageRoot);
|
||||
this._unInitializedDocuments.set(revivedUri.toString(), document);
|
||||
}
|
||||
@@ -1184,8 +1255,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
}
|
||||
|
||||
async $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise<void> {
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
async $executeNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void> {
|
||||
const document = this._documents.get(URI.revive(uri).toString());
|
||||
|
||||
if (!document) {
|
||||
return;
|
||||
@@ -1197,46 +1268,75 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
|
||||
if (provider.kernel) {
|
||||
if (cell) {
|
||||
return provider.kernel.executeCell(document, cell, token);
|
||||
return withToken(token => (provider.kernel!.executeCell as any)(document, cell, token));
|
||||
} else {
|
||||
return provider.kernel.executeAllCells(document, token);
|
||||
return withToken(token => (provider.kernel!.executeAllCells as any)(document, token));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async $executeNotebookKernelFromProvider(handle: number, uri: UriComponents, kernelId: string, cellHandle: number | undefined, token: CancellationToken): Promise<void> {
|
||||
await this._withAdapter(handle, uri, async (adapter, document) => {
|
||||
let cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
async $cancelNotebookByAttachedKernel(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void> {
|
||||
const document = this._documents.get(URI.revive(uri).toString());
|
||||
|
||||
return adapter.executeNotebook(kernelId, document, cell, token);
|
||||
if (!document) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._notebookContentProviders.has(viewType)) {
|
||||
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
const provider = this._notebookContentProviders.get(viewType)!.provider;
|
||||
|
||||
if (provider.kernel) {
|
||||
if (cell) {
|
||||
return provider.kernel.cancelCellExecution(document, cell);
|
||||
} else {
|
||||
return provider.kernel.cancelAllCellsExecution(document);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async $executeNotebookKernelFromProvider(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;
|
||||
|
||||
return adapter.executeNotebook(kernelId, document, cell);
|
||||
});
|
||||
}
|
||||
|
||||
async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): Promise<void> {
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
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;
|
||||
|
||||
return adapter.cancelNotebook(kernelId, document, cell);
|
||||
});
|
||||
}
|
||||
|
||||
async $executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void> {
|
||||
const document = this._documents.get(URI.revive(uri).toString());
|
||||
|
||||
if (!document || document.viewType !== viewType) {
|
||||
return;
|
||||
}
|
||||
|
||||
let kernelInfo = this._notebookKernels.get(kernelId);
|
||||
const kernelInfo = this._notebookKernels.get(kernelId);
|
||||
|
||||
if (!kernelInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
const cell = cellHandle !== undefined ? document.getCell(cellHandle) : undefined;
|
||||
|
||||
if (cell) {
|
||||
return kernelInfo.kernel.executeCell(document, cell, token);
|
||||
return withToken(token => (kernelInfo!.kernel.executeCell as any)(document, cell, token));
|
||||
} else {
|
||||
return kernelInfo.kernel.executeAllCells(document, token);
|
||||
return withToken(token => (kernelInfo!.kernel.executeAllCells as any)(document, token));
|
||||
}
|
||||
}
|
||||
|
||||
async $saveNotebook(viewType: string, uri: UriComponents, token: CancellationToken): Promise<boolean> {
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
const document = this._documents.get(URI.revive(uri).toString());
|
||||
if (!document) {
|
||||
return false;
|
||||
}
|
||||
@@ -1250,7 +1350,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
}
|
||||
|
||||
async $saveNotebookAs(viewType: string, uri: UriComponents, target: UriComponents, token: CancellationToken): Promise<boolean> {
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
const document = this._documents.get(URI.revive(uri).toString());
|
||||
if (!document) {
|
||||
return false;
|
||||
}
|
||||
@@ -1300,10 +1400,24 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
this._outputDisplayOrder = displayOrder;
|
||||
}
|
||||
|
||||
$acceptNotebookActiveKernelChange(event: { uri: UriComponents, providerHandle: number | undefined, kernelId: string | undefined; }) {
|
||||
if (event.providerHandle !== undefined) {
|
||||
this._withAdapter(event.providerHandle, event.uri, async (adapter, document) => {
|
||||
const kernel = event.kernelId ? adapter.getKernel(event.kernelId) : undefined;
|
||||
this._editors.forEach(editor => {
|
||||
if (editor.editor.document === document) {
|
||||
editor.editor.updateActiveKernel(kernel);
|
||||
}
|
||||
});
|
||||
this._onDidChangeActiveNotebookKernel.fire({ document, kernel });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove document - editor one on one mapping
|
||||
private _getEditorFromURI(uriComponents: UriComponents) {
|
||||
const uriStr = URI.revive(uriComponents).toString();
|
||||
let editor: { editor: ExtHostNotebookEditor } | undefined;
|
||||
let editor: { editor: ExtHostNotebookEditor; } | undefined;
|
||||
this._editors.forEach(e => {
|
||||
if (e.editor.uri.toString() === uriStr) {
|
||||
editor = e;
|
||||
@@ -1321,12 +1435,20 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
const document = this._documents.get(URI.revive(uriComponents).toString());
|
||||
|
||||
if (document) {
|
||||
document.accpetModelChanged(event);
|
||||
document.acceptModelChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
public $acceptModelSaved(uriComponents: UriComponents): void {
|
||||
const document = this._documents.get(URI.revive(uriComponents).toString());
|
||||
if (document) {
|
||||
// this.$acceptDirtyStateChanged(uriComponents, false);
|
||||
this._onDidSaveNotebookDocument.fire(document);
|
||||
}
|
||||
}
|
||||
|
||||
$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void {
|
||||
let editor = this._getEditorFromURI(uriComponents);
|
||||
const editor = this._getEditorFromURI(uriComponents);
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
@@ -1360,7 +1482,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
this._webviewComm.set(editorId, webComm);
|
||||
}
|
||||
|
||||
let editor = new ExtHostNotebookEditor(
|
||||
const editor = new ExtHostNotebookEditor(
|
||||
document.viewType,
|
||||
editorId,
|
||||
revivedUri,
|
||||
@@ -1394,7 +1516,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
delta.removedDocuments.forEach((uri) => {
|
||||
const revivedUri = URI.revive(uri);
|
||||
const revivedUriStr = revivedUri.toString();
|
||||
let document = this._documents.get(revivedUriStr);
|
||||
const document = this._documents.get(revivedUriStr);
|
||||
|
||||
if (document) {
|
||||
|
||||
@@ -1440,7 +1562,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
if (!this._documents.has(revivedUriStr)) {
|
||||
const that = this;
|
||||
|
||||
let document = this._unInitializedDocuments.get(revivedUriStr) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, {
|
||||
const document = this._unInitializedDocuments.get(revivedUriStr) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, {
|
||||
emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
|
||||
that._onDidChangeNotebookCells.fire(event);
|
||||
},
|
||||
@@ -1449,6 +1571,9 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
},
|
||||
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
|
||||
that._onDidChangeCellLanguage.fire(event);
|
||||
},
|
||||
emitCellMetadataChange(event: vscode.NotebookCellMetadataChangeEvent): void {
|
||||
that._onDidChangeCellMetadata.fire(event);
|
||||
}
|
||||
}, viewType, revivedUri, this, storageRoot);
|
||||
|
||||
@@ -1460,7 +1585,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
};
|
||||
}
|
||||
|
||||
document.accpetModelChanged({
|
||||
document.acceptModelChanged({
|
||||
kind: NotebookCellsChangeType.Initialize,
|
||||
versionId: modelData.versionId,
|
||||
changes: [[
|
||||
@@ -1508,7 +1633,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
});
|
||||
}
|
||||
|
||||
const removedEditors: { editor: ExtHostNotebookEditor }[] = [];
|
||||
const removedEditors: { editor: ExtHostNotebookEditor; }[] = [];
|
||||
|
||||
if (delta.removedEditors) {
|
||||
delta.removedEditors.forEach(editorid => {
|
||||
|
||||
@@ -41,7 +41,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
this._init();
|
||||
|
||||
this._disposables.add(extHostDocuments.onDidChangeDocument(e => {
|
||||
let cellIdx = this._cellUris.get(e.document.uri);
|
||||
const cellIdx = this._cellUris.get(e.document.uri);
|
||||
if (cellIdx !== undefined) {
|
||||
this._cellLengths.changeValue(cellIdx, this._cells[cellIdx].document.getText().length + 1);
|
||||
this._cellLines.changeValue(cellIdx, this._cells[cellIdx].document.lineCount);
|
||||
@@ -75,7 +75,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
this._cellUris = new ResourceMap();
|
||||
const cellLengths: number[] = [];
|
||||
const cellLineCounts: number[] = [];
|
||||
for (let cell of this._notebook.cells) {
|
||||
for (const cell of this._notebook.cells) {
|
||||
if (cell.cellKind === CellKind.Code && (!this._selector || score(this._selector, cell.uri, cell.language, true))) {
|
||||
this._cellUris.set(cell.uri, this._cells.length);
|
||||
this._cells.push(<ExtHostCell>cell);
|
||||
@@ -94,7 +94,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
getText(range?: vscode.Range): string {
|
||||
if (!range) {
|
||||
let result = '';
|
||||
for (let cell of this._cells) {
|
||||
for (const cell of this._cells) {
|
||||
result += cell.document.getText() + '\n';
|
||||
}
|
||||
// remove last newline again
|
||||
@@ -117,8 +117,8 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
} else if (startCell === endCell) {
|
||||
return startCell.document.getText(new types.Range(start.range.start, end.range.end));
|
||||
} else {
|
||||
let a = startCell.document.getText(new types.Range(start.range.start, new types.Position(startCell.document.lineCount, 0)));
|
||||
let b = endCell.document.getText(new types.Range(new types.Position(0, 0), end.range.end));
|
||||
const a = startCell.document.getText(new types.Range(start.range.start, new types.Position(startCell.document.lineCount, 0)));
|
||||
const b = endCell.document.getText(new types.Range(new types.Position(0, 0), end.range.end));
|
||||
return a + '\n' + b;
|
||||
}
|
||||
}
|
||||
@@ -139,7 +139,7 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
|
||||
const idx = this._cellUris.get(locationOrOffset.uri);
|
||||
if (idx !== undefined) {
|
||||
let line = this._cellLines.getAccumulatedValue(idx - 1);
|
||||
const line = this._cellLines.getAccumulatedValue(idx - 1);
|
||||
return new types.Position(line + locationOrOffset.range.start.line, locationOrOffset.range.start.character);
|
||||
}
|
||||
// do better?
|
||||
@@ -158,9 +158,9 @@ export class ExtHostNotebookConcatDocument implements vscode.NotebookConcatTextD
|
||||
endIdx = this._cellLines.getIndexOf(positionOrRange.end.line);
|
||||
}
|
||||
|
||||
let startPos = new types.Position(startIdx.remainder, positionOrRange.start.character);
|
||||
let endPos = new types.Position(endIdx.remainder, positionOrRange.end.character);
|
||||
let range = new types.Range(startPos, endPos);
|
||||
const startPos = new types.Position(startIdx.remainder, positionOrRange.start.character);
|
||||
const endPos = new types.Position(endIdx.remainder, positionOrRange.end.character);
|
||||
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));
|
||||
|
||||
@@ -18,12 +18,12 @@ export class ExtHostRpcService implements IExtHostRpcService {
|
||||
readonly getProxy: <T>(identifier: ProxyIdentifier<T>) => T;
|
||||
readonly set: <T, R extends T> (identifier: ProxyIdentifier<T>, instance: R) => R;
|
||||
readonly assertRegistered: (identifiers: ProxyIdentifier<any>[]) => void;
|
||||
readonly drain: () => Promise<void>;
|
||||
|
||||
constructor(rpcProtocol: IRPCProtocol) {
|
||||
this.getProxy = rpcProtocol.getProxy.bind(rpcProtocol);
|
||||
this.set = rpcProtocol.set.bind(rpcProtocol);
|
||||
this.assertRegistered = rpcProtocol.assertRegistered.bind(rpcProtocol);
|
||||
|
||||
this.drain = rpcProtocol.drain.bind(rpcProtocol);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { debounce } from 'vs/base/common/decorators';
|
||||
import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape } from './extHost.protocol';
|
||||
import { MainContext, MainThreadSCMShape, SCMRawResource, SCMRawResourceSplice, SCMRawResourceSplices, IMainContext, ExtHostSCMShape, ICommandDto, MainThreadTelemetryShape, SCMGroupFeatures } from './extHost.protocol';
|
||||
import { sortedDiff, equals } from 'vs/base/common/arrays';
|
||||
import { comparePaths } from 'vs/base/common/comparers';
|
||||
import type * as vscode from 'vscode';
|
||||
@@ -228,6 +228,9 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
|
||||
private readonly _onDidUpdateResourceStates = new Emitter<void>();
|
||||
readonly onDidUpdateResourceStates = this._onDidUpdateResourceStates.event;
|
||||
|
||||
private _disposed = false;
|
||||
get disposed(): boolean { return this._disposed; }
|
||||
private readonly _onDidDispose = new Emitter<void>();
|
||||
readonly onDidDispose = this._onDidDispose.event;
|
||||
|
||||
@@ -246,7 +249,13 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
get hideWhenEmpty(): boolean | undefined { return this._hideWhenEmpty; }
|
||||
set hideWhenEmpty(hideWhenEmpty: boolean | undefined) {
|
||||
this._hideWhenEmpty = hideWhenEmpty;
|
||||
this._proxy.$updateGroup(this._sourceControlHandle, this.handle, { hideWhenEmpty });
|
||||
this._proxy.$updateGroup(this._sourceControlHandle, this.handle, this.features);
|
||||
}
|
||||
|
||||
get features(): SCMGroupFeatures {
|
||||
return {
|
||||
hideWhenEmpty: this.hideWhenEmpty
|
||||
};
|
||||
}
|
||||
|
||||
get resourceStates(): vscode.SourceControlResourceState[] { return [...this._resourceStates]; }
|
||||
@@ -263,9 +272,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
private _sourceControlHandle: number,
|
||||
private _id: string,
|
||||
private _label: string,
|
||||
) {
|
||||
this._proxy.$registerGroup(_sourceControlHandle, this.handle, _id, _label);
|
||||
}
|
||||
) { }
|
||||
|
||||
getResourceState(handle: number): vscode.SourceControlResourceState | undefined {
|
||||
return this._resourceStatesMap.get(handle);
|
||||
@@ -311,8 +318,9 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
const tooltip = (r.decorations && r.decorations.tooltip) || '';
|
||||
const strikeThrough = r.decorations && !!r.decorations.strikeThrough;
|
||||
const faded = r.decorations && !!r.decorations.faded;
|
||||
const contextValue = r.contextValue || '';
|
||||
|
||||
const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded] as SCMRawResource;
|
||||
const rawResource = [handle, sourceUri, icons, tooltip, strikeThrough, faded, contextValue] as SCMRawResource;
|
||||
|
||||
return { rawResource, handle };
|
||||
});
|
||||
@@ -340,7 +348,7 @@ class ExtHostSourceControlResourceGroup implements vscode.SourceControlResourceG
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._proxy.$unregisterGroup(this._sourceControlHandle, this.handle);
|
||||
this._disposed = true;
|
||||
this._onDidDispose.fire();
|
||||
}
|
||||
}
|
||||
@@ -465,26 +473,51 @@ class ExtHostSourceControl implements vscode.SourceControl {
|
||||
this._proxy.$registerSourceControl(this.handle, _id, _label, _rootUri);
|
||||
}
|
||||
|
||||
private createdResourceGroups = new Map<ExtHostSourceControlResourceGroup, IDisposable>();
|
||||
private updatedResourceGroups = new Set<ExtHostSourceControlResourceGroup>();
|
||||
|
||||
createResourceGroup(id: string, label: string): ExtHostSourceControlResourceGroup {
|
||||
const group = new ExtHostSourceControlResourceGroup(this._proxy, this._commands, this.handle, id, label);
|
||||
|
||||
const updateListener = group.onDidUpdateResourceStates(() => {
|
||||
this.updatedResourceGroups.add(group);
|
||||
this.eventuallyUpdateResourceStates();
|
||||
});
|
||||
|
||||
Event.once(group.onDidDispose)(() => {
|
||||
this.updatedResourceGroups.delete(group);
|
||||
updateListener.dispose();
|
||||
this._groups.delete(group.handle);
|
||||
});
|
||||
|
||||
this._groups.set(group.handle, group);
|
||||
const disposable = Event.once(group.onDidDispose)(() => this.createdResourceGroups.delete(group));
|
||||
this.createdResourceGroups.set(group, disposable);
|
||||
this.eventuallyAddResourceGroups();
|
||||
return group;
|
||||
}
|
||||
|
||||
@debounce(100)
|
||||
eventuallyAddResourceGroups(): void {
|
||||
const groups: [number /*handle*/, string /*id*/, string /*label*/, SCMGroupFeatures][] = [];
|
||||
const splices: SCMRawResourceSplices[] = [];
|
||||
|
||||
for (const [group, disposable] of this.createdResourceGroups) {
|
||||
disposable.dispose();
|
||||
|
||||
const updateListener = group.onDidUpdateResourceStates(() => {
|
||||
this.updatedResourceGroups.add(group);
|
||||
this.eventuallyUpdateResourceStates();
|
||||
});
|
||||
|
||||
Event.once(group.onDidDispose)(() => {
|
||||
this.updatedResourceGroups.delete(group);
|
||||
updateListener.dispose();
|
||||
this._groups.delete(group.handle);
|
||||
this._proxy.$unregisterGroup(this.handle, group.handle);
|
||||
});
|
||||
|
||||
groups.push([group.handle, group.id, group.label, group.features]);
|
||||
|
||||
const snapshot = group._takeResourceStateSnapshot();
|
||||
|
||||
if (snapshot.length > 0) {
|
||||
splices.push([group.handle, snapshot]);
|
||||
}
|
||||
|
||||
this._groups.set(group.handle, group);
|
||||
}
|
||||
|
||||
this._proxy.$registerGroups(this.handle, groups, splices);
|
||||
}
|
||||
|
||||
@debounce(100)
|
||||
eventuallyUpdateResourceStates(): void {
|
||||
const splices: SCMRawResourceSplices[] = [];
|
||||
|
||||
@@ -419,6 +419,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask
|
||||
this._activeCustomExecutions2 = new Map<string, types.CustomExecution>();
|
||||
this._logService = logService;
|
||||
this._deprecationService = deprecationService;
|
||||
this._proxy.$registerSupportedExecutions(true);
|
||||
}
|
||||
|
||||
public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable {
|
||||
@@ -696,13 +697,11 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
|
||||
@IExtHostApiDeprecationService deprecationService: IExtHostApiDeprecationService
|
||||
) {
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService, deprecationService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
authority: initData.remote.authority,
|
||||
platform: Platform.PlatformToString(Platform.Platform.Web)
|
||||
});
|
||||
}
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
authority: '',
|
||||
platform: Platform.PlatformToString(Platform.Platform.Web)
|
||||
});
|
||||
}
|
||||
|
||||
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
|
||||
|
||||
@@ -20,6 +20,7 @@ import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib
|
||||
import { localize } from 'vs/nls';
|
||||
import { NotSupportedError } from 'vs/base/common/errors';
|
||||
import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
|
||||
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
|
||||
|
||||
@@ -320,6 +321,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
||||
private readonly _linkHandlers: Set<vscode.TerminalLinkHandler> = new Set();
|
||||
private readonly _linkProviders: Set<vscode.TerminalLinkProvider> = new Set();
|
||||
private readonly _terminalLinkCache: Map<number, Map<number, ICachedLinkEntry>> = new Map();
|
||||
private readonly _terminalLinkCancellationSource: Map<number, CancellationTokenSource> = new Map();
|
||||
|
||||
public get activeTerminal(): ExtHostTerminal | undefined { return this._activeTerminal; }
|
||||
public get terminals(): ExtHostTerminal[] { return this._terminals; }
|
||||
@@ -455,7 +457,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
||||
shellPath: shellLaunchConfigDto.executable,
|
||||
shellArgs: shellLaunchConfigDto.args,
|
||||
cwd: typeof shellLaunchConfigDto.cwd === 'string' ? shellLaunchConfigDto.cwd : URI.revive(shellLaunchConfigDto.cwd),
|
||||
env: shellLaunchConfigDto.env
|
||||
env: shellLaunchConfigDto.env,
|
||||
hideFromUser: shellLaunchConfigDto.hideFromUser
|
||||
};
|
||||
const terminal = new ExtHostTerminal(this._proxy, creationOptions, name, id);
|
||||
this._terminals.push(terminal);
|
||||
@@ -611,17 +614,33 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
||||
// when new links are provided.
|
||||
this._terminalLinkCache.delete(terminalId);
|
||||
|
||||
const oldToken = this._terminalLinkCancellationSource.get(terminalId);
|
||||
if (oldToken) {
|
||||
oldToken.dispose(true);
|
||||
}
|
||||
const cancellationSource = new CancellationTokenSource();
|
||||
this._terminalLinkCancellationSource.set(terminalId, cancellationSource);
|
||||
|
||||
const result: ITerminalLinkDto[] = [];
|
||||
const context: vscode.TerminalLinkContext = { terminal, line };
|
||||
const promises: vscode.ProviderResult<{ provider: vscode.TerminalLinkProvider, links: vscode.TerminalLink[] }>[] = [];
|
||||
|
||||
for (const provider of this._linkProviders) {
|
||||
promises.push(new Promise(async r => {
|
||||
const links = (await provider.provideTerminalLinks(context)) || [];
|
||||
r({ provider, links });
|
||||
cancellationSource.token.onCancellationRequested(() => r({ provider, links: [] }));
|
||||
const links = (await provider.provideTerminalLinks(context, cancellationSource.token)) || [];
|
||||
if (!cancellationSource.token.isCancellationRequested) {
|
||||
r({ provider, links });
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
const provideResults = await Promise.all(promises);
|
||||
|
||||
if (cancellationSource.token.isCancellationRequested) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const cacheLinkMap = new Map<number, ICachedLinkEntry>();
|
||||
for (const provideResult of provideResults) {
|
||||
if (provideResult && provideResult.links.length > 0) {
|
||||
|
||||
@@ -2739,6 +2739,11 @@ export enum NotebookCellRunState {
|
||||
Error = 4
|
||||
}
|
||||
|
||||
export enum NotebookRunState {
|
||||
Running = 1,
|
||||
Idle = 2
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Timeline
|
||||
|
||||
@@ -10,14 +10,166 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { forEach } from 'vs/base/common/collections';
|
||||
import { IExtensionPointUser, ExtensionMessageCollector, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { MenuId, MenuRegistry, ILocalizedString, IMenuItem, ICommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { MenuId, MenuRegistry, ILocalizedString, IMenuItem, ICommandAction, ISubmenuItem } from 'vs/platform/actions/common/actions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { index } from 'vs/base/common/arrays';
|
||||
|
||||
interface IAPIMenu {
|
||||
readonly key: string;
|
||||
readonly id: MenuId;
|
||||
readonly description: string;
|
||||
readonly proposed?: boolean; // defaults to false
|
||||
readonly supportsSubmenus?: boolean; // defaults to true
|
||||
}
|
||||
|
||||
const apiMenus: IAPIMenu[] = [
|
||||
{
|
||||
key: 'commandPalette',
|
||||
id: MenuId.CommandPalette,
|
||||
description: localize('menus.commandPalette', "The Command Palette"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'touchBar',
|
||||
id: MenuId.TouchBarContext,
|
||||
description: localize('menus.touchBar', "The touch bar (macOS only)"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'editor/title',
|
||||
id: MenuId.EditorTitle,
|
||||
description: localize('menus.editorTitle', "The editor title menu")
|
||||
},
|
||||
{
|
||||
key: 'editor/context',
|
||||
id: MenuId.EditorContext,
|
||||
description: localize('menus.editorContext', "The editor context menu")
|
||||
},
|
||||
{
|
||||
key: 'explorer/context',
|
||||
id: MenuId.ExplorerContext,
|
||||
description: localize('menus.explorerContext', "The file explorer context menu")
|
||||
},
|
||||
{
|
||||
key: 'editor/title/context',
|
||||
id: MenuId.EditorTitleContext,
|
||||
description: localize('menus.editorTabContext', "The editor tabs context menu")
|
||||
},
|
||||
{
|
||||
key: 'debug/callstack/context',
|
||||
id: MenuId.DebugCallStackContext,
|
||||
description: localize('menus.debugCallstackContext', "The debug callstack context menu")
|
||||
},
|
||||
{
|
||||
key: 'debug/toolBar',
|
||||
id: MenuId.DebugToolBar,
|
||||
description: localize('menus.debugToolBar', "The debug toolbar menu")
|
||||
},
|
||||
{
|
||||
key: 'menuBar/webNavigation',
|
||||
id: MenuId.MenubarWebNavigationMenu,
|
||||
description: localize('menus.webNavigation', "The top level navigational menu (web only)"),
|
||||
proposed: true,
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'scm/title',
|
||||
id: MenuId.SCMTitle,
|
||||
description: localize('menus.scmTitle', "The Source Control title menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/sourceControl',
|
||||
id: MenuId.SCMSourceControl,
|
||||
description: localize('menus.scmSourceControl', "The Source Control menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/resourceState/context',
|
||||
id: MenuId.SCMResourceContext,
|
||||
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/resourceFolder/context',
|
||||
id: MenuId.SCMResourceFolderContext,
|
||||
description: localize('menus.resourceStateContext', "The Source Control resource state context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/resourceGroup/context',
|
||||
id: MenuId.SCMResourceGroupContext,
|
||||
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu")
|
||||
},
|
||||
{
|
||||
key: 'scm/change/title',
|
||||
id: MenuId.SCMChangeContext,
|
||||
description: localize('menus.changeTitle', "The Source Control inline change menu")
|
||||
},
|
||||
{
|
||||
key: 'statusBar/windowIndicator',
|
||||
id: MenuId.StatusBarWindowIndicatorMenu,
|
||||
description: localize('menus.statusBarWindowIndicator', "The window indicator menu in the status bar"),
|
||||
proposed: true,
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'view/title',
|
||||
id: MenuId.ViewTitle,
|
||||
description: localize('view.viewTitle', "The contributed view title menu")
|
||||
},
|
||||
{
|
||||
key: 'view/item/context',
|
||||
id: MenuId.ViewItemContext,
|
||||
description: localize('view.itemContext', "The contributed view item context menu")
|
||||
},
|
||||
{
|
||||
key: 'comments/commentThread/title',
|
||||
id: MenuId.CommentThreadTitle,
|
||||
description: localize('commentThread.title', "The contributed comment thread title menu")
|
||||
},
|
||||
{
|
||||
key: 'comments/commentThread/context',
|
||||
id: MenuId.CommentThreadActions,
|
||||
description: localize('commentThread.actions', "The contributed comment thread context menu, rendered as buttons below the comment editor"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'comments/comment/title',
|
||||
id: MenuId.CommentTitle,
|
||||
description: localize('comment.title', "The contributed comment title menu")
|
||||
},
|
||||
{
|
||||
key: 'comments/comment/context',
|
||||
id: MenuId.CommentActions,
|
||||
description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"),
|
||||
supportsSubmenus: false
|
||||
},
|
||||
{
|
||||
key: 'notebook/cell/title',
|
||||
id: MenuId.NotebookCellTitle,
|
||||
description: localize('notebook.cell.title', "The contributed notebook cell title menu"),
|
||||
proposed: true
|
||||
},
|
||||
{
|
||||
key: 'extension/context',
|
||||
id: MenuId.ExtensionContext,
|
||||
description: localize('menus.extensionContext', "The extension context menu")
|
||||
},
|
||||
{
|
||||
key: 'timeline/title',
|
||||
id: MenuId.TimelineTitle,
|
||||
description: localize('view.timelineTitle', "The Timeline view title menu")
|
||||
},
|
||||
{
|
||||
key: 'timeline/item/context',
|
||||
id: MenuId.TimelineItemContext,
|
||||
description: localize('view.timelineContext', "The Timeline view item context menu")
|
||||
},
|
||||
];
|
||||
|
||||
namespace schema {
|
||||
|
||||
// --- menus contribution point
|
||||
// --- menus, submenus contribution point
|
||||
|
||||
export interface IUserFriendlyMenuItem {
|
||||
command: string;
|
||||
@@ -26,80 +178,102 @@ namespace schema {
|
||||
group?: string;
|
||||
}
|
||||
|
||||
export function parseMenuId(value: string): MenuId | undefined {
|
||||
switch (value) {
|
||||
case 'commandPalette': return MenuId.CommandPalette;
|
||||
case 'touchBar': return MenuId.TouchBarContext;
|
||||
case 'editor/title': return MenuId.EditorTitle;
|
||||
case 'editor/context': return MenuId.EditorContext;
|
||||
case 'explorer/context': return MenuId.ExplorerContext;
|
||||
case 'editor/title/context': return MenuId.EditorTitleContext;
|
||||
case 'debug/callstack/context': return MenuId.DebugCallStackContext;
|
||||
case 'debug/toolbar': return MenuId.DebugToolBar;
|
||||
case 'debug/toolBar': return MenuId.DebugToolBar;
|
||||
case 'menuBar/webNavigation': return MenuId.MenubarWebNavigationMenu;
|
||||
case 'scm/title': return MenuId.SCMTitle;
|
||||
case 'scm/sourceControl': return MenuId.SCMSourceControl;
|
||||
case 'scm/resourceState/context': return MenuId.SCMResourceContext;//
|
||||
case 'scm/resourceFolder/context': return MenuId.SCMResourceFolderContext;
|
||||
case 'scm/resourceGroup/context': return MenuId.SCMResourceGroupContext;
|
||||
case 'scm/change/title': return MenuId.SCMChangeContext;//
|
||||
case 'statusBar/windowIndicator': return MenuId.StatusBarWindowIndicatorMenu;
|
||||
case 'view/title': return MenuId.ViewTitle;
|
||||
case 'view/item/context': return MenuId.ViewItemContext;
|
||||
case 'comments/commentThread/title': return MenuId.CommentThreadTitle;
|
||||
case 'comments/commentThread/context': return MenuId.CommentThreadActions;
|
||||
case 'comments/comment/title': return MenuId.CommentTitle;
|
||||
case 'comments/comment/context': return MenuId.CommentActions;
|
||||
case 'notebook/cell/title': return MenuId.NotebookCellTitle;
|
||||
case 'extension/context': return MenuId.ExtensionContext;
|
||||
case 'timeline/title': return MenuId.TimelineTitle;
|
||||
case 'timeline/item/context': return MenuId.TimelineItemContext;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
export interface IUserFriendlySubmenuItem {
|
||||
submenu: string;
|
||||
when?: string;
|
||||
group?: string;
|
||||
}
|
||||
|
||||
export function isProposedAPI(menuId: MenuId): boolean {
|
||||
switch (menuId) {
|
||||
case MenuId.StatusBarWindowIndicatorMenu:
|
||||
case MenuId.MenubarWebNavigationMenu:
|
||||
case MenuId.NotebookCellTitle:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
export interface IUserFriendlySubmenu {
|
||||
id: string;
|
||||
label: string;
|
||||
icon?: IUserFriendlyIcon;
|
||||
}
|
||||
|
||||
export function isValidMenuItems(menu: IUserFriendlyMenuItem[], collector: ExtensionMessageCollector): boolean {
|
||||
if (!Array.isArray(menu)) {
|
||||
collector.error(localize('requirearray', "menu items must be an array"));
|
||||
export function isMenuItem(item: IUserFriendlyMenuItem | IUserFriendlySubmenuItem): item is IUserFriendlyMenuItem {
|
||||
return typeof (item as IUserFriendlyMenuItem).command === 'string';
|
||||
}
|
||||
|
||||
export function isValidMenuItem(item: IUserFriendlyMenuItem, collector: ExtensionMessageCollector): boolean {
|
||||
if (typeof item.command !== 'string') {
|
||||
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command'));
|
||||
return false;
|
||||
}
|
||||
if (item.alt && typeof item.alt !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'alt'));
|
||||
return false;
|
||||
}
|
||||
if (item.when && typeof item.when !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when'));
|
||||
return false;
|
||||
}
|
||||
if (item.group && typeof item.group !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'group'));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let item of menu) {
|
||||
if (typeof item.command !== 'string') {
|
||||
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'command'));
|
||||
return false;
|
||||
}
|
||||
if (item.alt && typeof item.alt !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'alt'));
|
||||
return false;
|
||||
}
|
||||
if (item.when && typeof item.when !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when'));
|
||||
return false;
|
||||
}
|
||||
if (item.group && typeof item.group !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'group'));
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isValidSubmenuItem(item: IUserFriendlySubmenuItem, collector: ExtensionMessageCollector): boolean {
|
||||
if (typeof item.submenu !== 'string') {
|
||||
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'submenu'));
|
||||
return false;
|
||||
}
|
||||
if (item.when && typeof item.when !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'when'));
|
||||
return false;
|
||||
}
|
||||
if (item.group && typeof item.group !== 'string') {
|
||||
collector.error(localize('optstring', "property `{0}` can be omitted or must be of type `string`", 'group'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isValidItems(items: (IUserFriendlyMenuItem | IUserFriendlySubmenuItem)[], collector: ExtensionMessageCollector): boolean {
|
||||
if (!Array.isArray(items)) {
|
||||
collector.error(localize('requirearray', "submenu items must be an array"));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let item of items) {
|
||||
if (isMenuItem(item)) {
|
||||
if (!isValidMenuItem(item, collector)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!isValidSubmenuItem(item, collector)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isValidSubmenu(submenu: IUserFriendlySubmenu, collector: ExtensionMessageCollector): boolean {
|
||||
if (typeof submenu !== 'object') {
|
||||
collector.error(localize('require', "submenu items must be an object"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof submenu.id !== 'string') {
|
||||
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'id'));
|
||||
return false;
|
||||
}
|
||||
if (typeof submenu.label !== 'string') {
|
||||
collector.error(localize('requirestring', "property `{0}` is mandatory and must be of type `string`", 'label'));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const menuItem: IJSONSchema = {
|
||||
type: 'object',
|
||||
required: ['command'],
|
||||
properties: {
|
||||
command: {
|
||||
description: localize('vscode.extension.contributes.menuItem.command', 'Identifier of the command to execute. The command must be declared in the \'commands\'-section'),
|
||||
@@ -114,144 +288,86 @@ namespace schema {
|
||||
type: 'string'
|
||||
},
|
||||
group: {
|
||||
description: localize('vscode.extension.contributes.menuItem.group', 'Group into which this command belongs'),
|
||||
description: localize('vscode.extension.contributes.menuItem.group', 'Group into which this item belongs'),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const submenuItem: IJSONSchema = {
|
||||
type: 'object',
|
||||
required: ['submenu'],
|
||||
properties: {
|
||||
submenu: {
|
||||
description: localize('vscode.extension.contributes.menuItem.submenu', 'Identifier of the submenu to display in this item.'),
|
||||
type: 'string'
|
||||
},
|
||||
when: {
|
||||
description: localize('vscode.extension.contributes.menuItem.when', 'Condition which must be true to show this item'),
|
||||
type: 'string'
|
||||
},
|
||||
group: {
|
||||
description: localize('vscode.extension.contributes.menuItem.group', 'Group into which this item belongs'),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const submenu: IJSONSchema = {
|
||||
type: 'object',
|
||||
required: ['id', 'label'],
|
||||
properties: {
|
||||
id: {
|
||||
description: localize('vscode.extension.contributes.submenu.id', 'Identifier of the menu to display as a submenu.'),
|
||||
type: 'string'
|
||||
},
|
||||
label: {
|
||||
description: localize('vscode.extension.contributes.submenu.label', 'The label of the menu item which leads to this submenu.'),
|
||||
type: 'string'
|
||||
},
|
||||
icon: {
|
||||
description: localize('vscode.extension.contributes.submenu.icon', '(Optional) Icon which is used to represent the submenu in the UI. Either a file path, an object with file paths for dark and light themes, or a theme icon references, like `\\$(zap)`'),
|
||||
anyOf: [{
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
light: {
|
||||
description: localize('vscode.extension.contributes.submenu.icon.light', 'Icon path when a light theme is used'),
|
||||
type: 'string'
|
||||
},
|
||||
dark: {
|
||||
description: localize('vscode.extension.contributes.submenu.icon.dark', 'Icon path when a dark theme is used'),
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const menusContribution: IJSONSchema = {
|
||||
description: localize('vscode.extension.contributes.menus', "Contributes menu items to the editor"),
|
||||
type: 'object',
|
||||
properties: {
|
||||
'commandPalette': {
|
||||
description: localize('menus.commandPalette', "The Command Palette"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'touchBar': {
|
||||
description: localize('menus.touchBar', "The touch bar (macOS only)"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'editor/title': {
|
||||
description: localize('menus.editorTitle', "The editor title menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'editor/context': {
|
||||
description: localize('menus.editorContext', "The editor context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'explorer/context': {
|
||||
description: localize('menus.explorerContext', "The file explorer context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'editor/title/context': {
|
||||
description: localize('menus.editorTabContext', "The editor tabs context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'debug/callstack/context': {
|
||||
description: localize('menus.debugCallstackContext', "The debug callstack context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'debug/toolBar': {
|
||||
description: localize('menus.debugToolBar', "The debug toolbar menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'menuBar/webNavigation': {
|
||||
description: localize('menus.webNavigation', "The top level navigational menu (web only)"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/title': {
|
||||
description: localize('menus.scmTitle', "The Source Control title menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/sourceControl': {
|
||||
description: localize('menus.scmSourceControl', "The Source Control menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/resourceGroup/context': {
|
||||
description: localize('menus.resourceGroupContext', "The Source Control resource group context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/resourceState/context': {
|
||||
description: localize('menus.resourceStateContext', "The Source Control resource state context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/resourceFolder/context': {
|
||||
description: localize('menus.resourceFolderContext', "The Source Control resource folder context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'scm/change/title': {
|
||||
description: localize('menus.changeTitle', "The Source Control inline change menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'view/title': {
|
||||
description: localize('view.viewTitle', "The contributed view title menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'view/item/context': {
|
||||
description: localize('view.itemContext', "The contributed view item context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'comments/commentThread/title': {
|
||||
description: localize('commentThread.title', "The contributed comment thread title menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'comments/commentThread/context': {
|
||||
description: localize('commentThread.actions', "The contributed comment thread context menu, rendered as buttons below the comment editor"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'comments/comment/title': {
|
||||
description: localize('comment.title', "The contributed comment title menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'comments/comment/context': {
|
||||
description: localize('comment.actions', "The contributed comment context menu, rendered as buttons below the comment editor"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'notebook/cell/title': {
|
||||
description: localize('notebook.cell.title', "The contributed notebook cell title menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'extension/context': {
|
||||
description: localize('menus.extensionContext', "The extension context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'timeline/title': {
|
||||
description: localize('view.timelineTitle', "The Timeline view title menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
'timeline/item/context': {
|
||||
description: localize('view.timelineContext', "The Timeline view item context menu"),
|
||||
type: 'array',
|
||||
items: menuItem
|
||||
},
|
||||
properties: index(apiMenus, menu => menu.key, menu => ({
|
||||
description: menu.proposed ? `(${localize('proposed', "Proposed API")}) ${menu.description}` : menu.description,
|
||||
type: 'array',
|
||||
items: menu.supportsSubmenus === false ? menuItem : { oneOf: [menuItem, submenuItem] }
|
||||
})),
|
||||
additionalProperties: {
|
||||
description: 'Submenu',
|
||||
type: 'array',
|
||||
items: { oneOf: [menuItem, submenuItem] }
|
||||
}
|
||||
};
|
||||
|
||||
export const submenusContribution: IJSONSchema = {
|
||||
description: localize('vscode.extension.contributes.submenus', "(Proposed API) Contributes submenu items to the editor"),
|
||||
type: 'array',
|
||||
items: submenu
|
||||
};
|
||||
|
||||
// --- commands contribution point
|
||||
|
||||
export interface IUserFriendlyCommand {
|
||||
@@ -430,74 +546,175 @@ commandsExtensionPoint.setHandler(extensions => {
|
||||
_commandRegistrations.add(MenuRegistry.addCommands(newCommands));
|
||||
});
|
||||
|
||||
const _menuRegistrations = new DisposableStore();
|
||||
interface IRegisteredSubmenu {
|
||||
readonly id: MenuId;
|
||||
readonly label: string;
|
||||
readonly icon?: { dark: URI; light?: URI; } | ThemeIcon;
|
||||
}
|
||||
|
||||
ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyMenuItem[] }>({
|
||||
extensionPoint: 'menus',
|
||||
jsonSchema: schema.menusContribution
|
||||
}).setHandler(extensions => {
|
||||
const _submenus = new Map<string, IRegisteredSubmenu>();
|
||||
|
||||
// remove all previous menu registrations
|
||||
_menuRegistrations.clear();
|
||||
const submenusExtensionPoint = ExtensionsRegistry.registerExtensionPoint<schema.IUserFriendlySubmenu[]>({
|
||||
extensionPoint: 'submenus',
|
||||
jsonSchema: schema.submenusContribution
|
||||
});
|
||||
|
||||
const items: { id: MenuId, item: IMenuItem }[] = [];
|
||||
submenusExtensionPoint.setHandler(extensions => {
|
||||
|
||||
_submenus.clear();
|
||||
|
||||
for (let extension of extensions) {
|
||||
const { value, collector } = extension;
|
||||
|
||||
forEach(value, entry => {
|
||||
if (!schema.isValidMenuItems(entry.value, collector)) {
|
||||
if (!schema.isValidSubmenu(entry.value, collector)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const menu = schema.parseMenuId(entry.key);
|
||||
if (typeof menu === 'undefined') {
|
||||
if (!entry.value.id) {
|
||||
collector.warn(localize('submenuId.invalid.id', "`{0}` is not a valid submenu identifier", entry.value.id));
|
||||
return;
|
||||
}
|
||||
if (!entry.value.label) {
|
||||
collector.warn(localize('submenuId.invalid.label', "`{0}` is not a valid submenu label", entry.value.label));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!extension.description.enableProposedApi) {
|
||||
collector.error(localize('submenu.proposedAPI.invalid', "Submenus are proposed API and are only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value));
|
||||
return;
|
||||
}
|
||||
|
||||
let absoluteIcon: { dark: URI; light?: URI; } | ThemeIcon | undefined;
|
||||
if (entry.value.icon) {
|
||||
if (typeof entry.value.icon === 'string') {
|
||||
absoluteIcon = ThemeIcon.fromString(entry.value.icon) || { dark: resources.joinPath(extension.description.extensionLocation, entry.value.icon) };
|
||||
} else {
|
||||
absoluteIcon = {
|
||||
dark: resources.joinPath(extension.description.extensionLocation, entry.value.icon.dark),
|
||||
light: resources.joinPath(extension.description.extensionLocation, entry.value.icon.light)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const item: IRegisteredSubmenu = {
|
||||
id: new MenuId(`api:${entry.value.id}`),
|
||||
label: entry.value.label,
|
||||
icon: absoluteIcon
|
||||
};
|
||||
|
||||
_submenus.set(entry.value.id, item);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const _apiMenusByKey = new Map(Iterable.map(Iterable.from(apiMenus), menu => ([menu.key, menu])));
|
||||
const _menuRegistrations = new DisposableStore();
|
||||
|
||||
const menusExtensionPoint = ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: (schema.IUserFriendlyMenuItem | schema.IUserFriendlySubmenuItem)[] }>({
|
||||
extensionPoint: 'menus',
|
||||
jsonSchema: schema.menusContribution,
|
||||
deps: [submenusExtensionPoint]
|
||||
});
|
||||
|
||||
menusExtensionPoint.setHandler(extensions => {
|
||||
|
||||
// remove all previous menu registrations
|
||||
_menuRegistrations.clear();
|
||||
|
||||
const items: { id: MenuId, item: IMenuItem | ISubmenuItem }[] = [];
|
||||
|
||||
for (let extension of extensions) {
|
||||
const { value, collector } = extension;
|
||||
|
||||
forEach(value, entry => {
|
||||
if (!schema.isValidItems(entry.value, collector)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let menu = _apiMenusByKey.get(entry.key);
|
||||
let isSubmenu = false;
|
||||
|
||||
if (!menu) {
|
||||
const submenu = _submenus.get(entry.key);
|
||||
|
||||
if (submenu) {
|
||||
menu = {
|
||||
key: entry.key,
|
||||
id: submenu.id,
|
||||
description: ''
|
||||
};
|
||||
isSubmenu = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!menu) {
|
||||
collector.warn(localize('menuId.invalid', "`{0}` is not a valid menu identifier", entry.key));
|
||||
return;
|
||||
}
|
||||
|
||||
if (schema.isProposedAPI(menu) && !extension.description.enableProposedApi) {
|
||||
if (menu.proposed && !extension.description.enableProposedApi) {
|
||||
collector.error(localize('proposedAPI.invalid', "{0} is a proposed menu identifier and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", entry.key, extension.description.identifier.value));
|
||||
return;
|
||||
}
|
||||
|
||||
for (let item of entry.value) {
|
||||
let command = MenuRegistry.getCommand(item.command);
|
||||
let alt = item.alt && MenuRegistry.getCommand(item.alt) || undefined;
|
||||
if (isSubmenu && !extension.description.enableProposedApi) {
|
||||
collector.error(localize('proposedAPI.invalid.submenu', "{0} is a submenu identifier and is only available when running out of dev or with the following command line switch: --enable-proposed-api {1}", entry.key, extension.description.identifier.value));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!command) {
|
||||
collector.error(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", item.command));
|
||||
continue;
|
||||
}
|
||||
if (item.alt && !alt) {
|
||||
collector.warn(localize('missing.altCommand', "Menu item references an alt-command `{0}` which is not defined in the 'commands' section.", item.alt));
|
||||
}
|
||||
if (item.command === item.alt) {
|
||||
collector.info(localize('dupe.command', "Menu item references the same command as default and alt-command"));
|
||||
for (const menuItem of entry.value) {
|
||||
let item: IMenuItem | ISubmenuItem;
|
||||
|
||||
if (schema.isMenuItem(menuItem)) {
|
||||
const command = MenuRegistry.getCommand(menuItem.command);
|
||||
const alt = menuItem.alt && MenuRegistry.getCommand(menuItem.alt) || undefined;
|
||||
|
||||
if (!command) {
|
||||
collector.error(localize('missing.command', "Menu item references a command `{0}` which is not defined in the 'commands' section.", menuItem.command));
|
||||
continue;
|
||||
}
|
||||
if (menuItem.alt && !alt) {
|
||||
collector.warn(localize('missing.altCommand', "Menu item references an alt-command `{0}` which is not defined in the 'commands' section.", menuItem.alt));
|
||||
}
|
||||
if (menuItem.command === menuItem.alt) {
|
||||
collector.info(localize('dupe.command', "Menu item references the same command as default and alt-command"));
|
||||
}
|
||||
|
||||
item = { command, alt, group: undefined, order: undefined, when: undefined };
|
||||
} else {
|
||||
if (!extension.description.enableProposedApi) {
|
||||
collector.error(localize('proposedAPI.invalid.submenureference', "Menu item references a submenu which is only available when running out of dev or with the following command line switch: --enable-proposed-api {0}", extension.description.identifier.value));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (menu.supportsSubmenus === false) {
|
||||
collector.error(localize('proposedAPI.unsupported.submenureference', "Menu item references a submenu for a menu which doesn't have submenu support."));
|
||||
continue;
|
||||
}
|
||||
|
||||
const submenu = _submenus.get(menuItem.submenu);
|
||||
|
||||
if (!submenu) {
|
||||
collector.error(localize('missing.submenu', "Menu item references a submenu `{0}` which is not defined in the 'submenus' section.", menuItem.submenu));
|
||||
continue;
|
||||
}
|
||||
|
||||
item = { submenu: submenu.id, icon: submenu.icon, title: submenu.label, group: undefined, order: undefined, when: undefined };
|
||||
}
|
||||
|
||||
let group: string | undefined;
|
||||
let order: number | undefined;
|
||||
if (item.group) {
|
||||
const idx = item.group.lastIndexOf('@');
|
||||
if (menuItem.group) {
|
||||
const idx = menuItem.group.lastIndexOf('@');
|
||||
if (idx > 0) {
|
||||
group = item.group.substr(0, idx);
|
||||
order = Number(item.group.substr(idx + 1)) || undefined;
|
||||
item.group = menuItem.group.substr(0, idx);
|
||||
item.order = Number(menuItem.group.substr(idx + 1)) || undefined;
|
||||
} else {
|
||||
group = item.group;
|
||||
item.group = menuItem.group;
|
||||
}
|
||||
}
|
||||
|
||||
items.push({
|
||||
id: menu,
|
||||
item: {
|
||||
command,
|
||||
alt,
|
||||
group,
|
||||
order,
|
||||
when: ContextKeyExpr.deserialize(item.when)
|
||||
}
|
||||
});
|
||||
item.when = ContextKeyExpr.deserialize(menuItem.when);
|
||||
items.push({ id: menu.id, item });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ExtHostDownloadService } from 'vs/workbench/api/node/extHostDownloadSer
|
||||
import { CLIServer } from 'vs/workbench/api/node/extHostCLIServer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
class NodeModuleRequireInterceptor extends RequireInterceptor {
|
||||
|
||||
@@ -76,6 +77,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
};
|
||||
}
|
||||
|
||||
protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined {
|
||||
return extensionDescription.main;
|
||||
}
|
||||
|
||||
protected _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
|
||||
if (module.scheme !== Schemas.file) {
|
||||
throw new Error(`Cannot load URI: '${module}', must be of file-scheme`);
|
||||
|
||||
@@ -47,6 +47,7 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
platform: process.platform
|
||||
});
|
||||
}
|
||||
this._proxy.$registerSupportedExecutions(true, true, true);
|
||||
}
|
||||
|
||||
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
|
||||
|
||||
@@ -200,7 +200,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHost
|
||||
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
class WorkerRequireInterceptor extends RequireInterceptor {
|
||||
|
||||
@@ -40,6 +41,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
await this._fakeModules.install();
|
||||
}
|
||||
|
||||
protected _getEntryPoint(extensionDescription: IExtensionDescription): string | undefined {
|
||||
return extensionDescription.browser;
|
||||
}
|
||||
|
||||
protected async _loadCommonJSModule<T>(module: URI, activationTimesBuilder: ExtensionActivationTimesBuilder): Promise<T> {
|
||||
|
||||
module = module.with({ path: ensureSuffix(module.path, '.js') });
|
||||
@@ -51,7 +56,10 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
|
||||
|
||||
// fetch JS sources as text and create a new function around it
|
||||
const source = await response.text();
|
||||
const initFn = new Function('module', 'exports', 'require', `${source}\n//# sourceURL=${module.toString(true)}`);
|
||||
// Here we append #vscode-extension to serve as a marker, such that source maps
|
||||
// can be adjusted for the extra wrapping function.
|
||||
const sourceURL = `${module.toString(true)}#vscode-extension`;
|
||||
const initFn = new Function('module', 'exports', 'require', `${source}\n//# sourceURL=${sourceURL}`);
|
||||
|
||||
// define commonjs globals: `module`, `exports`, and `require`
|
||||
const _exports = {};
|
||||
|
||||
Reference in New Issue
Block a user