Merge from vscode 4d91d96e5e121b38d33508cdef17868bab255eae

This commit is contained in:
ADS Merger
2020-06-18 04:32:54 +00:00
committed by AzureDataStudio
parent a971aee5bd
commit 5e7071e466
1002 changed files with 24201 additions and 13193 deletions

View File

@@ -41,6 +41,7 @@ import './mainThreadMessageService';
import './mainThreadOutputService';
import './mainThreadProgress';
import './mainThreadQuickOpen';
import './mainThreadRemoteConnectionData';
import './mainThreadSaveParticipant';
import './mainThreadSCM';
import './mainThreadSearch';

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
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';
@@ -12,15 +12,13 @@ import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContex
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import Severity from 'vs/base/common/severity';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { fromNow } from 'vs/base/common/date';
const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git'];
const VSO_ALLOWED_EXTENSIONS = ['github.vscode-pull-request-github', 'github.vscode-pull-request-github-insiders', 'vscode.git', 'ms-vsonline.vsonline'];
interface IAccountUsage {
extensionId: string;
@@ -71,7 +69,6 @@ function addAccountUsage(storageService: IStorageService, providerId: string, ac
}
export class MainThreadAuthenticationProvider extends Disposable {
private _sessionMenuItems = new Map<string, IDisposable[]>();
private _accounts = new Map<string, string[]>(); // Map account name to session ids
private _sessions = new Map<string, string>(); // Map account id to name
@@ -82,7 +79,9 @@ export class MainThreadAuthenticationProvider extends Disposable {
public readonly supportsMultipleAccounts: boolean,
private readonly notificationService: INotificationService,
private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
private readonly storageService: IStorageService
private readonly storageService: IStorageService,
private readonly quickInputService: IQuickInputService,
private readonly dialogService: IDialogService
) {
super();
}
@@ -95,11 +94,11 @@ export class MainThreadAuthenticationProvider extends Disposable {
return !!this._sessions.size;
}
private manageTrustedExtensions(quickInputService: IQuickInputService, storageService: IStorageService, accountName: string) {
const quickPick = quickInputService.createQuickPick<{ label: string, description: string, extension: AllowedExtension }>();
public manageTrustedExtensions(accountName: string) {
const quickPick = this.quickInputService.createQuickPick<{ label: string, description: string, extension: AllowedExtension }>();
quickPick.canSelectMany = true;
const allowedExtensions = readAllowedExtensions(storageService, this.id, accountName);
const usages = readAccountUsages(storageService, this.id, accountName);
const allowedExtensions = readAllowedExtensions(this.storageService, this.id, accountName);
const usages = readAccountUsages(this.storageService, this.id, accountName);
const items = allowedExtensions.map(extension => {
const usage = usages.find(usage => extension.id === usage.extensionId);
return {
@@ -118,7 +117,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
quickPick.onDidAccept(() => {
const updatedAllowedList = quickPick.selectedItems.map(item => item.extension);
storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL);
this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL);
quickPick.dispose();
});
@@ -146,69 +145,23 @@ export class MainThreadAuthenticationProvider extends Disposable {
this._accounts.set(session.account.displayName, [session.id]);
}
const menuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
group: '1_accounts',
command: {
id: `configureSessions${session.id}`,
title: `${session.account.displayName} (${this.displayName})`
},
order: 3
});
this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.displayName}`, version: 1 });
const manageCommand = CommandsRegistry.registerCommand({
id: `configureSessions${session.id}`,
handler: (accessor, args) => {
const quickInputService = accessor.get(IQuickInputService);
const storageService = accessor.get(IStorageService);
const dialogService = accessor.get(IDialogService);
const quickPick = quickInputService.createQuickPick();
const manage = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions");
const signOut = nls.localize('signOut', "Sign Out");
const items = ([{ label: manage }, { label: signOut }]);
quickPick.items = items;
quickPick.onDidAccept(e => {
const selected = quickPick.selectedItems[0];
if (selected.label === signOut) {
this.signOut(dialogService, session);
}
if (selected.label === manage) {
this.manageTrustedExtensions(quickInputService, storageService, session.account.displayName);
}
quickPick.dispose();
});
quickPick.onDidHide(_ => {
quickPick.dispose();
});
quickPick.show();
},
});
this._sessionMenuItems.set(session.account.displayName, [menuItem, manageCommand]);
}
async signOut(dialogService: IDialogService, session: modes.AuthenticationSession): Promise<void> {
const accountUsages = readAccountUsages(this.storageService, this.id, session.account.displayName);
const sessionsForAccount = this._accounts.get(session.account.displayName);
async signOut(accountName: string): Promise<void> {
const accountUsages = readAccountUsages(this.storageService, this.id, accountName);
const sessionsForAccount = this._accounts.get(accountName);
const result = await dialogService.confirm({
title: nls.localize('signOutConfirm', "Sign out of {0}", session.account.displayName),
const result = await this.dialogService.confirm({
title: nls.localize('signOutConfirm', "Sign out of {0}", accountName),
message: accountUsages.length
? nls.localize('signOutMessagve', "The account {0} has been used by: \n\n{1}\n\n Sign out of these features?", session.account.displayName, accountUsages.map(usage => usage.extensionName).join('\n'))
: nls.localize('signOutMessageSimple', "Sign out of {0}?", session.account.displayName)
? nls.localize('signOutMessagve', "The account {0} has been used by: \n\n{1}\n\n Sign out of these features?", accountName, accountUsages.map(usage => usage.extensionName).join('\n'))
: nls.localize('signOutMessageSimple', "Sign out of {0}?", accountName)
});
if (result.confirmed) {
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
removeAccountUsage(this.storageService, this.id, session.account.displayName);
removeAccountUsage(this.storageService, this.id, accountName);
}
}
@@ -230,11 +183,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
sessionsForAccount.splice(sessionIndex);
if (!sessionsForAccount.length) {
const disposeables = this._sessionMenuItems.get(accountName);
if (disposeables) {
disposeables.forEach(disposeable => disposeable.dispose());
this._sessionMenuItems.delete(accountName);
}
this._accounts.delete(accountName);
}
}
@@ -251,12 +199,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
await this._proxy.$logout(this.id, sessionId);
this.notificationService.info(nls.localize('signedOut', "Successfully signed out."));
}
dispose(): void {
super.dispose();
this._sessionMenuItems.forEach(item => item.forEach(d => d.dispose()));
this._sessionMenuItems.clear();
}
}
@extHostNamedCustomer(MainContext.MainThreadAuthentication)
@@ -294,7 +236,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
}
async $registerAuthenticationProvider(id: string, displayName: string, supportsMultipleAccounts: boolean): Promise<void> {
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService);
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService, this.quickInputService, this.dialogService);
await provider.initialize();
this.authenticationService.registerAuthenticationProvider(id, provider);
}

View File

@@ -135,7 +135,7 @@ export class MainThreadEditorInsets implements MainThreadEditorInsetsShape {
async $postMessage(handle: number, value: any): Promise<boolean> {
const inset = this.getInset(handle);
inset.webview.sendMessage(value);
inset.webview.postMessage(value);
return true;
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { ICommandService, CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ExtHostContext, MainThreadCommandsShape, ExtHostCommandsShape, MainContext, IExtHostContext } from '../common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { revive } from 'vs/base/common/marshalling';
@@ -28,7 +28,7 @@ export class MainThreadCommands implements MainThreadCommandsShape {
}
dispose() {
this._commandRegistrations.forEach(value => value.dispose());
dispose(this._commandRegistrations.values());
this._commandRegistrations.clear();
this._generateCommandsDocumentationRegistration.dispose();

View File

@@ -21,6 +21,7 @@ import { COMMENTS_VIEW_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comm
import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { Codicon } from 'vs/base/common/codicons';
export class MainThreadCommentThread implements modes.CommentThread {
@@ -458,6 +459,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true, donotShowContainerTitleWhenMergedWithContainer: true }]),
storageId: COMMENTS_VIEW_TITLE,
hideIfEmpty: true,
icon: Codicon.commentDiscussion.classNames,
order: 10,
}, ViewContainerLocation.Panel);
@@ -467,6 +469,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments
canToggleVisibility: false,
ctorDescriptor: new SyncDescriptor(CommentsPanel),
canMoveView: true,
containerIcon: Codicon.commentDiscussion.classNames,
focusCommand: {
id: 'workbench.action.focusCommentsPanel'
}

View File

@@ -229,7 +229,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb
const folderUri = folder ? uri.revive(folder) : undefined;
const launch = this.debugService.getConfigurationManager().getLaunch(folderUri);
const debugOptions: IDebugSessionOptions = {
noDebug: false,
noDebug: options.noDebug,
parentSession: this.getSession(options.parentSessionID),
repl: options.repl
};

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
@@ -35,8 +35,8 @@ export class MainThreadDocumentContentProviders implements MainThreadDocumentCon
}
dispose(): void {
this._resourceContentProvider.forEach(p => p.dispose());
this._pendingUpdate.forEach(source => source.dispose());
dispose(this._resourceContentProvider.values());
dispose(this._pendingUpdate.values());
}
$registerTextContentProvider(handle: number, scheme: string): void {

View File

@@ -16,7 +16,7 @@ import { ExtHostContext, ExtHostDocumentsShape, IExtHostContext, MainThreadDocum
import { ITextEditorModel } from 'vs/workbench/common/editor';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { toLocalResource, isEqualOrParent, extUri } from 'vs/base/common/resources';
import { toLocalResource, extUri, IExtUri } from 'vs/base/common/resources';
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
@@ -26,8 +26,9 @@ export class BoundModelReferenceCollection {
private _length = 0;
constructor(
private readonly _extUri: IExtUri,
private readonly _maxAge: number = 1000 * 60 * 3,
private readonly _maxLength: number = 1024 * 1024 * 80
private readonly _maxLength: number = 1024 * 1024 * 80,
) {
//
}
@@ -38,7 +39,7 @@ export class BoundModelReferenceCollection {
remove(uri: URI): void {
for (const entry of [...this._data] /* copy array because dispose will modify it */) {
if (isEqualOrParent(entry.uri, uri)) {
if (this._extUri.isEqualOrParent(entry.uri, uri)) {
entry.dispose();
}
}
@@ -85,7 +86,7 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
private _modelToDisposeMap: { [modelUrl: string]: IDisposable; };
private readonly _proxy: ExtHostDocumentsShape;
private readonly _modelIsSynced = new Set<string>();
private readonly _modelReferenceCollection = new BoundModelReferenceCollection();
private readonly _modelReferenceCollection: BoundModelReferenceCollection;
constructor(
documentsAndEditors: MainThreadDocumentsAndEditors,
@@ -105,11 +106,12 @@ export class MainThreadDocuments implements MainThreadDocumentsShape {
this._environmentService = environmentService;
this._uriIdentityService = uriIdentityService;
this._modelReferenceCollection = this._toDispose.add(new BoundModelReferenceCollection(uriIdentityService.extUri));
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocuments);
this._toDispose.add(documentsAndEditors.onDocumentAdd(models => models.forEach(this._onModelAdded, this)));
this._toDispose.add(documentsAndEditors.onDocumentRemove(urls => urls.forEach(this._onModelRemoved, this)));
this._toDispose.add(this._modelReferenceCollection);
this._toDispose.add(modelService.onModelModeChanged(this._onModelModeChanged, this));
this._toDispose.add(textFileService.files.onDidSave(e => {

View File

@@ -29,38 +29,39 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
namespace delta {
export function ofSets<T>(before: Set<T>, after: Set<T>): { removed: T[], added: T[] } {
const removed: T[] = [];
const added: T[] = [];
before.forEach(element => {
for (let element of before) {
if (!after.has(element)) {
removed.push(element);
}
});
after.forEach(element => {
}
for (let element of after) {
if (!before.has(element)) {
added.push(element);
}
});
}
return { removed, added };
}
export function ofMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[], added: V[] } {
const removed: V[] = [];
const added: V[] = [];
before.forEach((value, index) => {
for (let [index, value] of before) {
if (!after.has(index)) {
removed.push(value);
}
});
after.forEach((value, index) => {
}
for (let [index, value] of after) {
if (!before.has(index)) {
added.push(value);
}
});
}
return { removed, added };
}
}
@@ -265,11 +266,11 @@ class MainThreadDocumentAndEditorStateComputer {
}
if (candidate) {
editors.forEach(snapshot => {
for (const snapshot of editors.values()) {
if (candidate === snapshot.editor) {
activeEditor = snapshot.id;
}
});
}
}
}
@@ -331,6 +332,7 @@ export class MainThreadDocumentsAndEditors {
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService,
@IUriIdentityService uriIdentityService: IUriIdentityService,
@IClipboardService private readonly _clipboardService: IClipboardService,
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentsAndEditors);
@@ -365,7 +367,7 @@ export class MainThreadDocumentsAndEditors {
// added editors
for (const apiEditor of delta.addedEditors) {
const mainThreadEditor = new MainThreadTextEditor(apiEditor.id, apiEditor.editor.getModel(),
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService);
apiEditor.editor, { onGainedFocus() { }, onLostFocus() { } }, this._modelService, this._clipboardService);
this._textEditors.set(apiEditor.id, mainThreadEditor);
addedEditors.push(mainThreadEditor);

View File

@@ -17,6 +17,9 @@ import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorCon
import { IEditorPane } from 'vs/workbench/common/editor';
import { withNullAsUndefined } from 'vs/base/common/types';
import { equals } from 'vs/base/common/arrays';
import { CodeEditorStateFlag, EditorState } from 'vs/editor/browser/core/editorState';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { SnippetParser } from 'vs/editor/contrib/snippet/snippetParser';
export interface IFocusTracker {
onGainedFocus(): void;
@@ -159,6 +162,7 @@ export class MainThreadTextEditor {
private readonly _id: string;
private _model: ITextModel;
private readonly _modelService: IModelService;
private readonly _clipboardService: IClipboardService;
private readonly _modelListeners = new DisposableStore();
private _codeEditor: ICodeEditor | null;
private readonly _focusTracker: IFocusTracker;
@@ -172,7 +176,8 @@ export class MainThreadTextEditor {
model: ITextModel,
codeEditor: ICodeEditor,
focusTracker: IFocusTracker,
modelService: IModelService
modelService: IModelService,
clipboardService: IClipboardService,
) {
this._id = id;
this._model = model;
@@ -180,6 +185,7 @@ export class MainThreadTextEditor {
this._properties = null;
this._focusTracker = focusTracker;
this._modelService = modelService;
this._clipboardService = clipboardService;
this._onPropertiesChanged = new Emitter<IEditorPropertiesChangeData>();
@@ -454,12 +460,23 @@ export class MainThreadTextEditor {
return true;
}
insertSnippet(template: string, ranges: readonly IRange[], opts: IUndoStopOptions) {
async insertSnippet(template: string, ranges: readonly IRange[], opts: IUndoStopOptions) {
if (!this._codeEditor) {
if (!this._codeEditor || !this._codeEditor.hasModel()) {
return false;
}
// check if clipboard is required and only iff read it (async)
let clipboardText: string | undefined;
const needsTemplate = SnippetParser.guessNeedsClipboard(template);
if (needsTemplate) {
const state = new EditorState(this._codeEditor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position);
clipboardText = await this._clipboardService.readText();
if (!state.validate(this._codeEditor)) {
return false;
}
}
const snippetController = SnippetController2.get(this._codeEditor);
// // cancel previous snippet mode
@@ -471,7 +488,11 @@ export class MainThreadTextEditor {
this._codeEditor.focus();
// make modifications
snippetController.insert(template, { overwriteBefore: 0, overwriteAfter: 0, undoStopBefore: opts.undoStopBefore, undoStopAfter: opts.undoStopAfter });
snippetController.insert(template, {
overwriteBefore: 0, overwriteAfter: 0,
undoStopBefore: opts.undoStopBefore, undoStopAfter: opts.undoStopAfter,
clipboardText
});
return true;
}

View File

@@ -125,7 +125,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape {
// preserve pre 1.38 behaviour to not make group active when preserveFocus: true
// but make sure to restore the editor to fix https://github.com/microsoft/vscode/issues/79633
activation: options.preserveFocus ? EditorActivation.RESTORE : undefined,
ignoreOverrides: uri?.fsPath?.toLowerCase().endsWith('ipynb') || uri?.fsPath?.toLowerCase().endsWith('sql') ? false : true // {{SQL CARBON EDIT}}
override: uri?.fsPath?.toLowerCase().endsWith('ipynb') || uri?.fsPath?.toLowerCase().endsWith('sql') ? undefined : false // {{SQL CARBON EDIT}}
};
const input: IResourceEditorInput = {
@@ -306,7 +306,7 @@ CommandsRegistry.registerCommand('_workbench.openWith', (accessor: ServicesAcces
const [resource, id, options, position] = args;
const group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, position)) ?? editorGroupsService.activeGroup;
const textOptions = options ? { ...options, ignoreOverrides: true } : { ignoreOverrides: true };
const textOptions: ITextEditorOptions = options ? { ...options, override: false } : { override: false };
const input = editorService.createEditorInput({ resource });
return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService);

View File

@@ -25,7 +25,7 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
}
dispose(): void {
this._fileProvider.forEach(value => value.dispose());
dispose(this._fileProvider.values());
this._fileProvider.clear();
}

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
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';
@@ -17,6 +18,8 @@ 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';
export class MainThreadNotebookDocument extends Disposable {
private _textModel: NotebookTextModel;
@@ -29,11 +32,15 @@ export class MainThreadNotebookDocument extends Disposable {
private readonly _proxy: ExtHostNotebookShape,
public handle: number,
public viewType: string,
public supportBackup: boolean,
public uri: URI,
readonly notebookService: INotebookService
@INotebookService readonly notebookService: INotebookService,
@IUndoRedoService readonly undoRedoService: IUndoRedoService
) {
super();
this._textModel = new NotebookTextModel(handle, viewType, uri);
this._textModel = new NotebookTextModel(handle, viewType, supportBackup, uri);
this._register(this._textModel.onDidModelChangeProxy(e => {
this._proxy.$acceptModelChanged(this.uri, e);
this._proxy.$acceptEditorPropertiesChanged(uri, { selections: { selections: this._textModel.selections }, metadata: null });
@@ -44,7 +51,7 @@ export class MainThreadNotebookDocument extends Disposable {
}));
}
async applyEdit(modelVersionId: number, edits: ICellEditOperation[]): Promise<boolean> {
async applyEdit(modelVersionId: number, edits: ICellEditOperation[], emitToExtHost: boolean): Promise<boolean> {
await this.notebookService.transformEditsOutputs(this.textModel, edits);
return this._textModel.$applyEdit(modelVersionId, edits);
}
@@ -53,6 +60,22 @@ export class MainThreadNotebookDocument extends Disposable {
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();
super.dispose();
@@ -60,6 +83,22 @@ export class MainThreadNotebookDocument extends Disposable {
}
class DocumentAndEditorState {
static ofSets<T>(before: Set<T>, after: Set<T>): { removed: T[], added: T[] } {
const removed: T[] = [];
const added: T[] = [];
before.forEach(element => {
if (!after.has(element)) {
removed.push(element);
}
});
after.forEach(element => {
if (!before.has(element)) {
added.push(element);
}
});
return { removed, added };
}
static ofMaps<K, V>(before: Map<K, V>, after: Map<K, V>): { removed: V[], added: V[] } {
const removed: V[] = [];
const added: V[] = [];
@@ -86,15 +125,16 @@ class DocumentAndEditorState {
return {
addedDocuments: [],
addedEditors: apiEditors
addedEditors: apiEditors,
visibleEditors: [...after.visibleEditors].map(editor => editor[0])
};
}
// const documentDelta = delta.ofSets(before.documents, after.documents);
const documentDelta = DocumentAndEditorState.ofSets(before.documents, after.documents);
const editorDelta = DocumentAndEditorState.ofMaps(before.textEditors, after.textEditors);
const addedAPIEditors = editorDelta.added.map(add => ({
id: add.getId(),
documentUri: add.uri!,
selections: add.textModel!.selections
selections: add.textModel!.selections || []
}));
const removedAPIEditors = editorDelta.removed.map(removed => removed.getId());
@@ -102,17 +142,46 @@ class DocumentAndEditorState {
// const oldActiveEditor = before.activeEditor !== after.activeEditor ? before.activeEditor : undefined;
const newActiveEditor = before.activeEditor !== after.activeEditor ? after.activeEditor : undefined;
const visibleEditorDelta = DocumentAndEditorState.ofMaps(before.visibleEditors, after.visibleEditors);
return {
addedDocuments: documentDelta.added.map(e => {
return {
viewType: e.viewType,
handle: e.handle,
uri: e.uri,
metadata: e.metadata,
versionId: e.versionId,
cells: e.cells.map(cell => ({
handle: cell.handle,
uri: cell.uri,
source: cell.textBuffer.getLinesContent(),
language: cell.language,
cellKind: cell.cellKind,
outputs: cell.outputs,
metadata: cell.metadata
})),
// attachedEditor: editorId ? {
// id: editorId,
// selections: document.textModel.selections
// } : undefined
};
}),
removedDocuments: documentDelta.removed.map(e => e.uri),
addedEditors: addedAPIEditors,
removedEditors: removedAPIEditors,
newActiveEditor: newActiveEditor
newActiveEditor: newActiveEditor,
visibleEditors: visibleEditorDelta.added.length === 0 && visibleEditorDelta.removed.length === 0
? undefined
: [...after.visibleEditors].map(editor => editor[0])
};
}
constructor(
readonly documents: Set<URI>,
readonly documents: Set<NotebookTextModel>,
readonly textEditors: Map<string, IEditor>,
readonly activeEditor: string | null | undefined,
readonly visibleEditors: Map<string, IEditor>
) {
//
}
@@ -132,7 +201,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
@INotebookService private _notebookService: INotebookService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IEditorService private readonly editorService: IEditorService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IInstantiationService private readonly _instantiationService: IInstantiationService
) {
super();
@@ -150,29 +220,77 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
return false;
}
private _isDeltaEmpty(delta: INotebookDocumentsAndEditorsDelta) {
if (delta.addedDocuments !== undefined && delta.addedDocuments.length > 0) {
return false;
}
if (delta.removedDocuments !== undefined && delta.removedDocuments.length > 0) {
return false;
}
if (delta.addedEditors !== undefined && delta.addedEditors.length > 0) {
return false;
}
if (delta.removedEditors !== undefined && delta.removedEditors.length > 0) {
return false;
}
if (delta.visibleEditors !== undefined && delta.visibleEditors.length > 0) {
return false;
}
if (delta.newActiveEditor !== undefined) {
return false;
}
return true;
}
private _emitDelta(delta: INotebookDocumentsAndEditorsDelta) {
if (this._isDeltaEmpty(delta)) {
return;
}
this._proxy.$acceptDocumentAndEditorsDelta(delta);
}
registerListeners() {
this._notebookService.listNotebookEditors().forEach((e) => {
this._addNotebookEditor(e);
});
this._register(this._notebookService.onDidChangeActiveEditor(e => {
this._proxy.$acceptDocumentAndEditorsDelta({
newActiveEditor: e
});
this._updateState();
}));
this._register(this._notebookService.onDidChangeVisibleEditors(e => {
this._proxy.$acceptDocumentAndEditorsDelta({
visibleEditors: e
});
if (this._notebookProviders.size > 0) {
if (!this._currentState) {
// no current state means we didn't even create editors in ext host yet.
return;
}
// we can't simply update visibleEditors as we need to check if we should create editors first.
this._updateState();
}
}));
this._register(this._notebookService.onNotebookEditorAdd(editor => {
this._addNotebookEditor(editor);
}));
this._register(this._notebookService.onNotebookEditorRemove(editor => {
this._removeNotebookEditor(editor);
this._register(this._notebookService.onNotebookEditorsRemove(editors => {
this._removeNotebookEditor(editors);
}));
this._register(this._notebookService.onNotebookDocumentAdd(() => {
this._updateState();
}));
this._register(this._notebookService.onNotebookDocumentRemove(() => {
this._updateState();
}));
const updateOrder = () => {
@@ -201,9 +319,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
}
async addNotebookDocument(data: INotebookModelAddedData) {
await this._proxy.$acceptDocumentAndEditorsDelta({
addedDocuments: [data]
});
this._updateState();
}
private _addNotebookEditor(e: IEditor) {
@@ -219,39 +335,58 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
this._updateState(notebookEditor);
}
private _removeNotebookEditor(e: IEditor) {
const sub = this._toDisposeOnEditorRemove.get(e.getId());
if (sub) {
this._toDisposeOnEditorRemove.delete(e.getId());
sub.dispose();
this._updateState();
}
private _removeNotebookEditor(editors: IEditor[]) {
editors.forEach(e => {
const sub = this._toDisposeOnEditorRemove.get(e.getId());
if (sub) {
this._toDisposeOnEditorRemove.delete(e.getId());
sub.dispose();
}
});
this._updateState();
}
private async _updateState(focusedNotebookEditor?: IEditor) {
const documents = new Set<URI>();
this._notebookService.listNotebookDocuments().forEach(document => {
documents.add(document.uri);
});
const editors = new Map<string, IEditor>();
let activeEditor: string | null = null;
for (const editor of this._notebookService.listNotebookEditors()) {
if (editor.hasModel()) {
editors.set(editor.getId(), editor);
if (editor.hasFocus()) {
activeEditor = editor.getId();
}
}
const activeEditorPane = this.editorService.activeEditorPane as any | undefined;
if (activeEditorPane?.isNotebookEditor) {
const notebookEditor = (activeEditorPane.getControl() as INotebookEditor);
activeEditor = notebookEditor && notebookEditor.hasModel() ? notebookEditor!.getId() : null;
}
if (!activeEditor && focusedNotebookEditor) {
const documentEditorsMap = new Map<string, IEditor>();
const editors = new Map<string, IEditor>();
this._notebookService.listNotebookEditors().forEach(editor => {
if (editor.hasModel()) {
editors.set(editor.getId(), editor);
documentEditorsMap.set(editor.textModel!.uri.toString(), editor);
}
});
const visibleEditorsMap = new Map<string, IEditor>();
this.editorService.visibleEditorPanes.forEach(editor => {
if ((editor as any).isNotebookEditor) {
const nbEditorWidget = (editor as any).getControl() as INotebookEditor;
if (nbEditorWidget && editors.has(nbEditorWidget.getId())) {
visibleEditorsMap.set(nbEditorWidget.getId(), nbEditorWidget);
}
}
});
const documents = new Set<NotebookTextModel>();
this._notebookService.listNotebookDocuments().forEach(document => {
documents.add(document);
});
if (!activeEditor && focusedNotebookEditor && focusedNotebookEditor.hasModel()) {
activeEditor = focusedNotebookEditor.getId();
}
// editors always have view model attached, which means there is already a document in exthost.
const newState = new DocumentAndEditorState(documents, editors, activeEditor);
const newState = new DocumentAndEditorState(documents, editors, activeEditor, visibleEditorsMap);
const delta = DocumentAndEditorState.compute(this._currentState, newState);
// const isEmptyChange = (!delta.addedDocuments || delta.addedDocuments.length === 0)
// && (!delta.removedDocuments || delta.removedDocuments.length === 0)
@@ -261,7 +396,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
// if (!isEmptyChange) {
this._currentState = newState;
await this._proxy.$acceptDocumentAndEditorsDelta(delta);
await this._emitDelta(delta);
// }
}
@@ -275,8 +410,8 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
this._notebookService.unregisterNotebookRenderer(id);
}
async $registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, kernel: INotebookKernelInfoDto | undefined): Promise<void> {
let controller = new MainThreadNotebookController(this._proxy, this, viewType, kernel, this._notebookService);
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);
return;
@@ -355,6 +490,16 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
return false;
}
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void {
let controller = this._notebookProviders.get(viewType);
controller?.handleEdit(resource, editId, label);
}
$onContentChange(resource: UriComponents, viewType: string): void {
let controller = this._notebookProviders.get(viewType);
controller?.handleNotebookChange(resource);
}
}
export class MainThreadNotebookController implements IMainNotebookController {
@@ -365,13 +510,15 @@ export class MainThreadNotebookController implements IMainNotebookController {
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): Promise<NotebookTextModel | undefined> {
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) {
@@ -383,15 +530,15 @@ export class MainThreadNotebookController implements IMainNotebookController {
mainthreadNotebook.textModel.languages = data.languages;
mainthreadNotebook.textModel.metadata = data.metadata;
mainthreadNotebook.textModel.$applyEdit(mainthreadNotebook.textModel.versionId, [
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);
}
return mainthreadNotebook.textModel;
}
let document = new MainThreadNotebookDocument(this._proxy, MainThreadNotebookController.documentHandle++, viewType, uri, this.notebookService);
let document = this._instantiationService.createInstance(MainThreadNotebookDocument, this._proxy, MainThreadNotebookController.documentHandle++, viewType, this._supportBackup, uri);
this._mapping.set(document.uri.toString(), document);
if (backup) {
@@ -399,14 +546,16 @@ export class MainThreadNotebookController implements IMainNotebookController {
document.textModel.metadata = backup.metadata;
document.textModel.languages = backup.languages;
document.textModel.$applyEdit(document.textModel.versionId, [
// 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);
// create document in ext host with cells data
await this._mainThreadNotebook.addNotebookDocument({
viewType: document.viewType,
handle: document.handle,
@@ -432,7 +581,7 @@ export class MainThreadNotebookController implements IMainNotebookController {
}
// open notebook document
const data = await this._proxy.$resolveNotebookData(viewType, uri);
const data = await this._proxy.$resolveNotebookData(viewType, uri, backupId);
if (!data) {
return undefined; // {{SQL CARBON EDIT}}
}
@@ -473,11 +622,15 @@ export class MainThreadNotebookController implements IMainNotebookController {
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);
return await mainthreadNotebook.applyEdit(modelVersionId, edits, true);
}
return false;
@@ -503,6 +656,7 @@ export class MainThreadNotebookController implements IMainNotebookController {
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());
@@ -515,6 +669,11 @@ export class MainThreadNotebookController implements IMainNotebookController {
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);
@@ -540,7 +699,11 @@ export class MainThreadNotebookController implements IMainNotebookController {
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;
}
}

View File

@@ -0,0 +1,35 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ExtHostContext, IExtHostContext, ExtHostExtensionServiceShape } from '../common/extHost.protocol';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { Disposable } from 'vs/base/common/lifecycle';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@extHostCustomer
export class MainThreadRemoteConnectionData extends Disposable {
private readonly _proxy: ExtHostExtensionServiceShape;
constructor(
extHostContext: IExtHostContext,
@IWorkbenchEnvironmentService protected readonly _environmentService: IWorkbenchEnvironmentService,
@IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostExtensionService);
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
if (remoteAuthority) {
this._register(remoteAuthorityResolverService.onDidChangeConnectionData(() => {
const connectionData = remoteAuthorityResolverService.getConnectionData(remoteAuthority);
if (connectionData) {
this._proxy.$updateRemoteConnectionData(connectionData);
}
}));
}
}
}

View File

@@ -5,7 +5,6 @@
import { CancellationToken } from 'vs/base/common/cancellation';
import { dispose, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { values } from 'vs/base/common/map';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
@@ -139,7 +138,7 @@ class RemoteSearchProvider implements ISearchResultProvider, IDisposable {
return Promise.resolve(searchP).then((result: ISearchCompleteStats) => {
this._searches.delete(search.id);
return { results: values(search.matches), stats: result.stats, limitHit: result.limitHit };
return { results: Array.from(search.matches.values()), stats: result.stats, limitHit: result.limitHit };
}, err => {
this._searches.delete(search.id);
return Promise.reject(err);

View File

@@ -32,7 +32,7 @@ export class MainThreadStatusBar implements MainThreadStatusBarShape {
if (accessibilityInformation) {
ariaLabel = accessibilityInformation.label;
} else {
ariaLabel = text.indexOf('$(') === -1 ? text : tooltip || text;
ariaLabel = text && text.indexOf('$(') === -1 ? text : tooltip || text;
}
const entry: IStatusbarEntry = { text, tooltip, command, color, ariaLabel };

View File

@@ -510,41 +510,41 @@ export class MainThreadTask implements MainThreadTaskShape {
});
}
public $executeTask(value: TaskHandleDTO | TaskDTO): Promise<TaskExecutionDTO> {
return new Promise<TaskExecutionDTO>((resolve, reject) => {
if (TaskHandleDTO.is(value)) {
const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
if (workspaceFolder) {
this._taskService.getTask(workspaceFolder, value.id, true).then((task: Task | undefined) => {
if (!task) {
reject(new Error('Task not found'));
} else {
this._taskService.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
const result: TaskExecutionDTO = {
id: value.id,
task: TaskDTO.from(task)
};
resolve(result);
}
}, (_error) => {
reject(new Error('Task not found'));
});
} else {
reject(new Error('No workspace folder'));
public async $getTaskExecution(value: TaskHandleDTO | TaskDTO): Promise<TaskExecutionDTO> {
if (TaskHandleDTO.is(value)) {
const workspaceFolder = typeof value.workspaceFolder === 'string' ? value.workspaceFolder : this._workspaceContextServer.getWorkspaceFolder(URI.revive(value.workspaceFolder));
if (workspaceFolder) {
const task = await this._taskService.getTask(workspaceFolder, value.id, true);
if (task) {
return {
id: task._id,
task: TaskDTO.from(task)
};
}
throw new Error('Task not found');
} else {
const task = TaskDTO.to(value, this._workspaceContextServer, true)!;
this._taskService.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
const result: TaskExecutionDTO = {
id: task._id,
task: TaskDTO.from(task)
};
resolve(result);
throw new Error('No workspace folder');
}
} else {
const task = TaskDTO.to(value, this._workspaceContextServer, true)!;
return {
id: task._id,
task: TaskDTO.from(task)
};
}
}
public $executeTask(value: TaskDTO): Promise<TaskExecutionDTO> {
return new Promise<TaskExecutionDTO>((resolve, reject) => {
const task = TaskDTO.to(value, this._workspaceContextServer, true)!;
this._taskService.run(task).then(undefined, reason => {
// eat the error, it has already been surfaced to the user and we don't care about it here
});
const result: TaskExecutionDTO = {
id: task._id,
task: TaskDTO.from(task)
};
resolve(result);
});
}

View File

@@ -22,8 +22,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
private _proxy: ExtHostTerminalServiceShape;
private _remoteAuthority: string | null;
private readonly _toDispose = new DisposableStore();
private readonly _terminalProcesses = new Map<number, Promise<ITerminalProcessExtHostProxy>>();
private readonly _terminalProcessesReady = new Map<number, (proxy: ITerminalProcessExtHostProxy) => void>();
private readonly _terminalProcessProxies = new Map<number, ITerminalProcessExtHostProxy>();
private _dataEventTracker: TerminalDataEventTracker | undefined;
private _linkHandler: IDisposable | undefined;
@@ -106,7 +105,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
isExtensionTerminal: launchConfig.isExtensionTerminal
};
const terminal = this._terminalService.createTerminal(shellLaunchConfig);
this._terminalProcesses.set(terminal.id, new Promise<ITerminalProcessExtHostProxy>(r => this._terminalProcessesReady.set(terminal.id, r)));
return Promise.resolve({
id: terminal.id,
name: terminal.title
@@ -232,13 +230,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
const proxy = request.proxy;
const ready = this._terminalProcessesReady.get(proxy.terminalId);
if (ready) {
ready(proxy);
this._terminalProcessesReady.delete(proxy.terminalId);
} else {
this._terminalProcesses.set(proxy.terminalId, Promise.resolve(proxy));
}
this._terminalProcessProxies.set(proxy.terminalId, proxy);
const shellLaunchConfigDto: IShellLaunchConfigDto = {
name: request.shellLaunchConfig.name,
executable: request.shellLaunchConfig.executable,
@@ -246,7 +238,16 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
cwd: request.shellLaunchConfig.cwd,
env: request.shellLaunchConfig.env
};
this._proxy.$spawnExtHostProcess(proxy.terminalId, shellLaunchConfigDto, request.activeWorkspaceRootUri, request.cols, request.rows, request.isWorkspaceShellAllowed);
this._proxy.$spawnExtHostProcess(
proxy.terminalId,
shellLaunchConfigDto,
request.activeWorkspaceRootUri,
request.cols,
request.rows,
request.isWorkspaceShellAllowed
).then(request.callback);
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
proxy.onResize(dimensions => this._proxy.$acceptProcessResize(proxy.terminalId, dimensions.cols, dimensions.rows));
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
@@ -257,13 +258,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
private _onRequestStartExtensionTerminal(request: IStartExtensionTerminalRequest): void {
const proxy = request.proxy;
const ready = this._terminalProcessesReady.get(proxy.terminalId);
if (!ready) {
this._terminalProcesses.set(proxy.terminalId, Promise.resolve(proxy));
} else {
ready(proxy);
this._terminalProcessesReady.delete(proxy.terminalId);
}
this._terminalProcessProxies.set(proxy.terminalId, proxy);
// Note that onReisze is not being listened to here as it needs to fire when max dimensions
// change, excluding the dimension override
@@ -271,7 +266,12 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
columns: request.cols,
rows: request.rows
} : undefined;
this._proxy.$startExtensionTerminal(proxy.terminalId, initialDimensions);
this._proxy.$startExtensionTerminal(
proxy.terminalId,
initialDimensions
).then(request.callback);
proxy.onInput(data => this._proxy.$acceptProcessInput(proxy.terminalId, data));
proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(proxy.terminalId, immediate));
proxy.onRequestCwd(() => this._proxy.$acceptProcessRequestCwd(proxy.terminalId));
@@ -280,38 +280,38 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
public $sendProcessTitle(terminalId: number, title: string): void {
this._getTerminalProcess(terminalId).then(e => e.emitTitle(title));
this._getTerminalProcess(terminalId).emitTitle(title);
}
public $sendProcessData(terminalId: number, data: string): void {
this._getTerminalProcess(terminalId).then(e => e.emitData(data));
this._getTerminalProcess(terminalId).emitData(data);
}
public $sendProcessReady(terminalId: number, pid: number, cwd: string): void {
this._getTerminalProcess(terminalId).then(e => e.emitReady(pid, cwd));
this._getTerminalProcess(terminalId).emitReady(pid, cwd);
}
public $sendProcessExit(terminalId: number, exitCode: number | undefined): void {
this._getTerminalProcess(terminalId).then(e => e.emitExit(exitCode));
this._terminalProcesses.delete(terminalId);
this._getTerminalProcess(terminalId).emitExit(exitCode);
this._terminalProcessProxies.delete(terminalId);
}
public $sendOverrideDimensions(terminalId: number, dimensions: ITerminalDimensions | undefined): void {
this._getTerminalProcess(terminalId).then(e => e.emitOverrideDimensions(dimensions));
this._getTerminalProcess(terminalId).emitOverrideDimensions(dimensions);
}
public $sendProcessInitialCwd(terminalId: number, initialCwd: string): void {
this._getTerminalProcess(terminalId).then(e => e.emitInitialCwd(initialCwd));
this._getTerminalProcess(terminalId).emitInitialCwd(initialCwd);
}
public $sendProcessCwd(terminalId: number, cwd: string): void {
this._getTerminalProcess(terminalId).then(e => e.emitCwd(cwd));
this._getTerminalProcess(terminalId).emitCwd(cwd);
}
public $sendResolvedLaunchConfig(terminalId: number, shellLaunchConfig: IShellLaunchConfig): void {
const instance = this._terminalService.getInstanceFromId(terminalId);
if (instance) {
this._getTerminalProcess(terminalId).then(e => e.emitResolvedShellLaunchConfig(shellLaunchConfig));
this._getTerminalProcess(terminalId).emitResolvedShellLaunchConfig(shellLaunchConfig);
}
}
@@ -324,7 +324,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
sw.stop();
sum += sw.elapsed();
}
this._getTerminalProcess(terminalId).then(e => e.emitLatency(sum / COUNT));
this._getTerminalProcess(terminalId).emitLatency(sum / COUNT);
}
private _isPrimaryExtHost(): boolean {
@@ -349,8 +349,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
}
private _getTerminalProcess(terminalId: number): Promise<ITerminalProcessExtHostProxy> {
const terminal = this._terminalProcesses.get(terminalId);
private _getTerminalProcess(terminalId: number): ITerminalProcessExtHostProxy {
const terminal = this._terminalProcessProxies.get(terminalId);
if (!terminal) {
throw new Error(`Unknown terminal: ${terminalId}`);
}

View File

@@ -5,7 +5,7 @@
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedError } from 'vs/base/common/errors';
import { onUnexpectedError, isPromiseCanceledError } 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';
@@ -252,7 +252,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
public async $postMessage(handle: extHostProtocol.WebviewPanelHandle, message: any): Promise<boolean> {
const webview = this.getWebviewInput(handle);
webview.webview.sendMessage(message);
webview.webview.postMessage(message);
return true;
}
@@ -280,8 +280,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
if (webviewInput.webview.state) {
try {
state = JSON.parse(webviewInput.webview.state);
} catch {
// noop
} catch (e) {
console.error('Could not load webview state', e, webviewInput.webview.state);
}
}
@@ -925,7 +925,12 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
this._backupId = backupId;
}
} catch (e) {
// Make sure state has not changed in the meantime
if (isPromiseCanceledError(e)) {
// This is expected
throw e;
}
// Otherwise it could be a real error. Make sure state has not changed in the meantime.
if (this._hotExitState === pendingState) {
this._hotExitState = HotExitState.NotAllowed;
}

View File

@@ -97,12 +97,12 @@ CommandsRegistry.registerCommand({
export class DiffAPICommand {
public static readonly ID = 'vscode.diff';
public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions): Promise<any> {
public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: typeConverters.TextEditorOpenOptions): Promise<any> {
return executor.executeCommand('_workbench.diff', [
left, right,
label,
undefined,
typeConverters.TextEditorOptions.from(options),
typeConverters.TextEditorOpenOptions.from(options),
options ? typeConverters.ViewColumn.from(options.viewColumn) : undefined
]);
}
@@ -111,7 +111,7 @@ CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand
export class OpenAPICommand {
public static readonly ID = 'vscode.open';
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Promise<any> {
public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions, label?: string): Promise<any> {
let options: ITextEditorOptions | undefined;
let position: EditorViewColumn | undefined;
@@ -119,7 +119,7 @@ export class OpenAPICommand {
if (typeof columnOrOptions === 'number') {
position = typeConverters.ViewColumn.from(columnOrOptions);
} else {
options = typeConverters.TextEditorOptions.from(columnOrOptions);
options = typeConverters.TextEditorOpenOptions.from(columnOrOptions);
position = typeConverters.ViewColumn.from(columnOrOptions.viewColumn);
}
}
@@ -136,14 +136,14 @@ CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand
export class OpenWithAPICommand {
public static readonly ID = 'vscode.openWith';
public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions): Promise<any> {
public static execute(executor: ICommandsExecutor, resource: URI, viewType: string, columnOrOptions?: vscode.ViewColumn | typeConverters.TextEditorOpenOptions): Promise<any> {
let options: ITextEditorOptions | undefined;
let position: EditorViewColumn | undefined;
if (typeof columnOrOptions === 'number') {
position = typeConverters.ViewColumn.from(columnOrOptions);
} else if (typeof columnOrOptions !== 'undefined') {
options = typeConverters.TextEditorOptions.from(columnOrOptions);
options = typeConverters.TextEditorOpenOptions.from(columnOrOptions);
}
return executor.executeCommand('_workbench.openWith', [

View File

@@ -93,12 +93,14 @@ const defaultConfigurationExtPoint = ExtensionsRegistry.registerExtensionPoint<I
description: nls.localize('vscode.extension.contributes.defaultConfiguration', 'Contributes default editor configuration settings by language.'),
type: 'object',
patternProperties: {
'\\[.*\\]$': {
'^\\[.*\\]$': {
type: 'object',
default: {},
$ref: resourceLanguageSettingsSchemaId,
}
}
},
errorMessage: nls.localize('config.property.defaultConfiguration.languageExpected', "Language selector expected (e.g. [\"java\"])"),
additionalProperties: false
}
});
defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => {

View File

@@ -135,7 +135,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol));
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment));
const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors, initData.environment, extensionStoragePaths));
const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol));
const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol));
const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands));
@@ -458,16 +458,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
get terminals() {
return extHostTerminalService.terminals;
},
showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Thenable<vscode.TextEditor> {
let documentPromise: Promise<vscode.TextDocument>;
if (URI.isUri(documentOrUri)) {
documentPromise = Promise.resolve(workspace.openTextDocument(documentOrUri));
} else {
documentPromise = Promise.resolve(<vscode.TextDocument>documentOrUri);
}
return documentPromise.then(document => {
return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus);
});
async showTextDocument(documentOrUri: vscode.TextDocument | vscode.Uri, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, preserveFocus?: boolean): Promise<vscode.TextEditor> {
const document = await (URI.isUri(documentOrUri)
? Promise.resolve(workspace.openTextDocument(documentOrUri))
: Promise.resolve(<vscode.TextDocument>documentOrUri));
return extHostEditors.showTextDocument(document, columnOrOptions, preserveFocus);
},
createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType {
return extHostEditors.createTextEditorDecorationType(options);
@@ -602,11 +598,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer);
},
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions } = {}) => {
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
},
registerCustomEditorProvider2: (viewType: string, provider: vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => {
checkProposedApiEnabled(extension);
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomTextEditorProvider | vscode.CustomReadonlyEditorProvider, options: { webviewOptions?: vscode.WebviewPanelOptions, supportsMultipleEditorsPerDocument?: boolean } = {}) => {
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
},
registerDecorationProvider(provider: vscode.DecorationProvider) {
@@ -946,10 +938,16 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.onDidCloseNotebookDocument;
},
get notebookDocuments(): vscode.NotebookDocument[] {
checkProposedApiEnabled(extension);
return extHostNotebook.notebookDocuments;
},
get visibleNotebookEditors() {
checkProposedApiEnabled(extension);
return extHostNotebook.visibleNotebookEditors;
},
get onDidChangeVisibleNotebookEditors() {
checkProposedApiEnabled(extension);
return extHostNotebook.onDidChangeVisibleNotebookEditors;
},
registerNotebookContentProvider: (viewType: string, provider: vscode.NotebookContentProvider) => {
@@ -964,10 +962,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
checkProposedApiEnabled(extension);
return extHostNotebook.registerNotebookOutputRenderer(type, extension, outputFilter, renderer);
},
get activeNotebookDocument(): vscode.NotebookDocument | undefined {
checkProposedApiEnabled(extension);
return extHostNotebook.activeNotebookDocument;
},
get activeNotebookEditor(): vscode.NotebookEditor | undefined {
checkProposedApiEnabled(extension);
return extHostNotebook.activeNotebookEditor;

View File

@@ -31,7 +31,7 @@ import { LogLevel } from 'vs/platform/log/common/log';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import * as quickInput from 'vs/platform/quickinput/common/quickInput';
import { RemoteAuthorityResolverErrorCode, ResolverResult, TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorityResolverErrorCode, ResolverResult, TunnelDescription, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import * as statusbar from 'vs/workbench/services/statusbar/common/statusbar';
import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
@@ -41,7 +41,7 @@ import * as tasks from 'vs/workbench/api/common/shared/tasks';
import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
import { ITextQueryBuilderOptions } from 'vs/workbench/contrib/search/common/queryBuilder';
import { ITerminalDimensions, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalDimensions, IShellLaunchConfig, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier';
import * as search from 'vs/workbench/services/search/common/search';
@@ -51,7 +51,7 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
import { revive } from 'vs/base/common/marshalling';
import { INotebookMimeTypeSelector, IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, IOutputRenderRequest, IOutputRenderResponse, IRawOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookMimeTypeSelector, IProcessedOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent, NotebookDataDto, INotebookKernelInfoDto, IMainCellDto, IOutputRenderRequest, IOutputRenderResponse } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
import { Dto } from 'vs/base/common/types';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
@@ -103,7 +103,7 @@ export interface IInitData {
logsLocation: URI;
logFile: URI;
autoStart: boolean;
remote: { isRemote: boolean; authority: string | undefined; };
remote: { isRemote: boolean; authority: string | undefined; connectionData: IRemoteConnectionData | null; };
uiKind: UIKind;
}
@@ -697,11 +697,11 @@ export type NotebookCellsSplice = [
export type NotebookCellOutputsSplice = [
number /* start */,
number /* delete count */,
IRawOutput[]
IProcessedOutput[]
];
export interface MainThreadNotebookShape extends IDisposable {
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, kernelInfoDto: INotebookKernelInfoDto | undefined): Promise<void>;
$registerNotebookProvider(extension: NotebookExtensionDescription, viewType: string, supportBackup: boolean, kernelInfoDto: INotebookKernelInfoDto | undefined): Promise<void>;
$onNotebookChange(viewType: string, resource: UriComponents): Promise<void>;
$unregisterNotebookProvider(viewType: string): Promise<void>;
$registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, preloads: UriComponents[]): Promise<void>;
@@ -714,6 +714,9 @@ export interface MainThreadNotebookShape extends IDisposable {
$updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise<void>;
$spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void>;
$postMessage(handle: number, value: any): Promise<boolean>;
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
$onContentChange(resource: UriComponents, viewType: string): void;
}
export interface MainThreadUrlsShape extends IDisposable {
@@ -778,7 +781,8 @@ export interface MainThreadTaskShape extends IDisposable {
$registerTaskProvider(handle: number, type: string): Promise<void>;
$unregisterTaskProvider(handle: number): Promise<void>;
$fetchTasks(filter?: tasks.TaskFilterDTO): Promise<tasks.TaskDTO[]>;
$executeTask(task: tasks.TaskHandleDTO | tasks.TaskDTO): Promise<tasks.TaskExecutionDTO>;
$getTaskExecution(value: tasks.TaskHandleDTO | tasks.TaskDTO): Promise<tasks.TaskExecutionDTO>;
$executeTask(task: tasks.TaskDTO): Promise<tasks.TaskExecutionDTO>;
$terminateTask(id: string): Promise<void>;
$registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void;
$customExecutionComplete(id: string, result?: number): Promise<void>;
@@ -855,6 +859,7 @@ export interface IDebugConfiguration {
export interface IStartDebuggingOptions {
parentSessionID?: DebugSessionUUID;
repl?: IDebugSessionReplMode;
noDebug?: boolean;
}
export interface MainThreadDebugServiceShape extends IDisposable {
@@ -1051,6 +1056,7 @@ export interface ExtHostExtensionServiceShape {
$activateByEvent(activationEvent: string): Promise<void>;
$activate(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean>;
$setRemoteEnvironment(env: { [key: string]: string | null; }): Promise<void>;
$updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void>;
$deltaExtensions(toAdd: IExtensionDescription[], toRemove: ExtensionIdentifier[]): Promise<void>;
@@ -1393,8 +1399,8 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalTitleChange(id: number, name: string): void;
$acceptTerminalDimensions(id: number, cols: number, rows: number): void;
$acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void;
$spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void;
$startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): void;
$spawnExtHostProcess(id: number, shellLaunchConfig: IShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
$startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<ITerminalLaunchError | undefined>;
$acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void;
$acceptProcessShutdown(id: number, immediate: boolean): void;
@@ -1583,11 +1589,13 @@ export interface INotebookDocumentsAndEditorsDelta {
}
export interface ExtHostNotebookShape {
$resolveNotebookData(viewType: string, uri: UriComponents): Promise<NotebookDataDto | undefined>;
$resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto | undefined>;
$resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<void>;
$executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, useAttachedKernel: boolean, token: CancellationToken): Promise<void>;
$executeNotebook2(kernelId: string, viewType: string, uri: UriComponents, cellHandle: number | undefined, token: CancellationToken): 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;
$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>;
@@ -1595,6 +1603,9 @@ export interface ExtHostNotebookShape {
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void;
$acceptEditorPropertiesChanged(uriComponents: UriComponents, data: INotebookEditorPropertiesChangeData): void;
$acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta): Promise<void>;
$undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
$redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void>;
}
export interface ExtHostStorageShape {

View File

@@ -10,7 +10,7 @@ import * as extHostProtocol from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
export interface IExtHostApiDeprecationService {
_serviceBrand: undefined;
readonly _serviceBrand: undefined;
report(apiId: string, extension: IExtensionDescription, migrationSuggestion: string): void;
}
@@ -19,7 +19,7 @@ export const IExtHostApiDeprecationService = createDecorator<IExtHostApiDeprecat
export class ExtHostApiDeprecationService implements IExtHostApiDeprecationService {
_serviceBrand: undefined;
declare readonly _serviceBrand: undefined;
private readonly _reportedUsages = new Set<string>();
private readonly _telemetryShape: extHostProtocol.MainThreadTelemetryShape;
@@ -63,7 +63,7 @@ export class ExtHostApiDeprecationService implements IExtHostApiDeprecationServi
export const NullApiDeprecationService = Object.freeze(new class implements IExtHostApiDeprecationService {
_serviceBrand: undefined;
declare readonly _serviceBrand: undefined;
public report(_apiId: string, _extension: IExtensionDescription, _warningMessage: string): void {
// noop

View File

@@ -28,11 +28,11 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape {
// dispose editor inset whenever the hosting editor goes away
this._disposables.add(_editors.onDidChangeVisibleTextEditors(() => {
const visibleEditor = _editors.getVisibleTextEditors();
this._insets.forEach(value => {
for (const value of this._insets.values()) {
if (visibleEditor.indexOf(value.editor) < 0) {
value.inset.dispose(); // will remove from `this._insets`
}
});
}
}));
}

View File

@@ -204,12 +204,12 @@ export class ExtHostCommands implements ExtHostCommandsShape {
$getContributedCommandHandlerDescriptions(): Promise<{ [id: string]: string | ICommandHandlerDescription }> {
const result: { [id: string]: string | ICommandHandlerDescription } = Object.create(null);
this._commands.forEach((command, id) => {
for (let [id, command] of this._commands) {
let { description } = command;
if (description) {
result[id] = description;
}
});
}
return Promise.resolve(result);
}
}

View File

@@ -57,7 +57,7 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape {
asDebugSourceUri(source: vscode.DebugProtocolSource, session?: vscode.DebugSession): vscode.Uri;
}
export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDebugServiceShape {
export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDebugServiceShape {
readonly _serviceBrand: undefined;
@@ -295,7 +295,8 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
public startDebugging(folder: vscode.WorkspaceFolder | undefined, nameOrConfig: string | vscode.DebugConfiguration, options: vscode.DebugSessionOptions): Promise<boolean> {
return this._debugServiceProxy.$startDebugging(folder ? folder.uri : undefined, nameOrConfig, {
parentSessionID: options.parentSession ? options.parentSession.id : undefined,
repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate'
repl: options.consoleMode === DebugConsoleMode.MergeWithParent ? 'mergeWithParent' : 'separate',
noDebug: options.noDebug
});
}
@@ -372,9 +373,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb
return Promise.resolve(undefined);
}
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
return new ExtHostVariableResolverService(folders, editorService, configurationService);
}
protected abstract createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService;
public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise<IConfig> {
if (!this._variableResolver) {
@@ -973,7 +972,7 @@ export class ExtHostDebugConsole implements vscode.DebugConsole {
export class ExtHostVariableResolverService extends AbstractVariableResolverService {
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) {
constructor(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors | undefined, configurationService: ExtHostConfigProvider, env?: IProcessEnvironment) {
super({
getFolderUri: (folderName: string): URI | undefined => {
const found = folders.filter(f => f.name === folderName);
@@ -992,27 +991,33 @@ export class ExtHostVariableResolverService extends AbstractVariableResolverServ
return env ? env['VSCODE_EXEC_PATH'] : undefined;
},
getFilePath: (): string | undefined => {
const activeEditor = editorService.activeEditor();
if (activeEditor) {
return path.normalize(activeEditor.document.uri.fsPath);
if (editorService) {
const activeEditor = editorService.activeEditor();
if (activeEditor) {
return path.normalize(activeEditor.document.uri.fsPath);
}
}
return undefined;
},
getSelectedText: (): string | undefined => {
const activeEditor = editorService.activeEditor();
if (activeEditor && !activeEditor.selection.isEmpty) {
return activeEditor.document.getText(activeEditor.selection);
if (editorService) {
const activeEditor = editorService.activeEditor();
if (activeEditor && !activeEditor.selection.isEmpty) {
return activeEditor.document.getText(activeEditor.selection);
}
}
return undefined;
},
getLineNumber: (): string | undefined => {
const activeEditor = editorService.activeEditor();
if (activeEditor) {
return String(activeEditor.selection.end.line + 1);
if (editorService) {
const activeEditor = editorService.activeEditor();
if (activeEditor) {
return String(activeEditor.selection.end.line + 1);
}
}
return undefined;
}
}, env);
}, env, !editorService);
}
}
@@ -1103,4 +1108,8 @@ export class WorkerExtHostDebugService extends ExtHostDebugServiceBase {
) {
super(extHostRpcService, workspaceService, extensionService, editorsService, configurationService, commandService);
}
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
return new ExtHostVariableResolverService(folders, editorService, configurationService);
}
}

View File

@@ -173,9 +173,9 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection {
forEach(callback: (uri: URI, diagnostics: ReadonlyArray<vscode.Diagnostic>, collection: DiagnosticCollection) => any, thisArg?: any): void {
this._checkDisposed();
this._data.forEach((value, uri) => {
for (let uri of this._data.keys()) {
callback.apply(thisArg, [uri, this.get(uri), this]);
});
}
}
get(uri: URI): ReadonlyArray<vscode.Diagnostic> {
@@ -307,7 +307,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
} else {
const index = new Map<string, number>();
const res: [vscode.Uri, vscode.Diagnostic[]][] = [];
this._collections.forEach(collection => {
for (const collection of this._collections.values()) {
collection.forEach((uri, diagnostics) => {
let idx = index.get(uri.toString());
if (typeof idx === 'undefined') {
@@ -317,18 +317,18 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape {
}
res[idx][1] = res[idx][1].concat(...diagnostics);
});
});
}
return res;
}
}
private _getDiagnostics(resource: vscode.Uri): ReadonlyArray<vscode.Diagnostic> {
let res: vscode.Diagnostic[] = [];
this._collections.forEach(collection => {
for (let collection of this._collections.values()) {
if (collection.has(resource)) {
res = res.concat(collection.get(resource));
}
});
}
return res;
}

View File

@@ -14,6 +14,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor';
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import { ILogService } from 'vs/platform/log/common/log';
import { ResourceMap } from 'vs/base/common/map';
export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape {
@@ -22,7 +23,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
private _activeEditorId: string | null = null;
private readonly _editors = new Map<string, ExtHostTextEditor>();
private readonly _documents = new Map<string, ExtHostDocumentData>();
private readonly _documents = new ResourceMap<ExtHostDocumentData>();
private readonly _onDidAddDocuments = new Emitter<ExtHostDocumentData[]>();
private readonly _onDidRemoveDocuments = new Emitter<ExtHostDocumentData[]>();
@@ -48,9 +49,8 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
if (delta.removedDocuments) {
for (const uriComponent of delta.removedDocuments) {
const uri = URI.revive(uriComponent);
const id = uri.toString();
const data = this._documents.get(id);
this._documents.delete(id);
const data = this._documents.get(uri);
this._documents.delete(uri);
if (data) {
removedDocuments.push(data);
}
@@ -60,7 +60,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
if (delta.addedDocuments) {
for (const data of delta.addedDocuments) {
const resource = URI.revive(data.uri);
assert.ok(!this._documents.has(resource.toString()), `document '${resource} already exists!'`);
assert.ok(!this._documents.has(resource), `document '${resource} already exists!'`);
const documentData = new ExtHostDocumentData(
this._extHostRpc.getProxy(MainContext.MainThreadDocuments),
@@ -71,7 +71,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
data.versionId,
data.isDirty
);
this._documents.set(resource.toString(), documentData);
this._documents.set(resource, documentData);
addedDocuments.push(documentData);
}
}
@@ -89,10 +89,10 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
if (delta.addedEditors) {
for (const data of delta.addedEditors) {
const resource = URI.revive(data.documentUri);
assert.ok(this._documents.has(resource.toString()), `document '${resource}' does not exist`);
assert.ok(this._documents.has(resource), `document '${resource}' does not exist`);
assert.ok(!this._editors.has(data.id), `editor '${data.id}' already exists!`);
const documentData = this._documents.get(resource.toString())!;
const documentData = this._documents.get(resource)!;
const editor = new ExtHostTextEditor(
data.id,
this._extHostRpc.getProxy(MainContext.MainThreadTextEditors),
@@ -132,13 +132,11 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
}
getDocument(uri: URI): ExtHostDocumentData | undefined {
return this._documents.get(uri.toString());
return this._documents.get(uri);
}
allDocuments(): ExtHostDocumentData[] {
const result: ExtHostDocumentData[] = [];
this._documents.forEach(data => result.push(data));
return result;
return [...this._documents.values()];
}
getEditor(id: string): ExtHostTextEditor | undefined {
@@ -154,9 +152,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha
}
allEditors(): ExtHostTextEditor[] {
const result: ExtHostTextEditor[] = [];
this._editors.forEach(data => result.push(data));
return result;
return [...this._editors.values()];
}
}

View File

@@ -6,8 +6,8 @@
import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import { originalFSPath, joinPath } from 'vs/base/common/resources';
import { Barrier } from 'vs/base/common/async';
import { dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Barrier, timeout } from 'vs/base/common/async';
import { dispose, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
@@ -16,7 +16,7 @@ import { ExtHostConfiguration, IExtHostConfiguration } from 'vs/workbench/api/co
import { ActivatedExtension, EmptyExtension, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionModule, HostExtension, ExtensionActivationTimesFragment } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostStorage, IExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { ExtensionActivationError, checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionActivationError } from 'vs/workbench/services/extensions/common/extensions';
import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors';
@@ -26,7 +26,7 @@ import { Schemas } from 'vs/base/common/network';
import { VSBuffer } from 'vs/base/common/buffer';
import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { RemoteAuthorityResolverError, ExtensionMode } from 'vs/workbench/api/common/extHostTypes';
import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ResolvedAuthority, ResolvedOptions, RemoteAuthorityResolverErrorCode, IRemoteConnectionData } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
@@ -34,6 +34,7 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService';
import { Emitter, Event } from 'vs/base/common/event';
interface ITestRunner {
/** Old test runner API, as exported from `vscode/lib/testrunner` */
@@ -48,7 +49,7 @@ interface INewTestRunner {
export const IHostUtils = createDecorator<IHostUtils>('IHostUtils');
export interface IHostUtils {
_serviceBrand: undefined;
readonly _serviceBrand: undefined;
exit(code?: number): void;
exists(path: string): Promise<boolean>;
realpath(path: string): Promise<string>;
@@ -65,12 +66,15 @@ type TelemetryActivationEventFragment = {
reasonId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' };
};
export abstract class AbstractExtHostExtensionService implements ExtHostExtensionServiceShape {
export abstract class AbstractExtHostExtensionService extends Disposable implements ExtHostExtensionServiceShape {
readonly _serviceBrand: undefined;
private static readonly WORKSPACE_CONTAINS_TIMEOUT = 7000;
private readonly _onDidChangeRemoteConnectionData = this._register(new Emitter<void>());
public readonly onDidChangeRemoteConnectionData = this._onDidChangeRemoteConnectionData.event;
protected readonly _hostUtils: IHostUtils;
protected readonly _initData: IInitData;
protected readonly _extHostContext: IExtHostRpcService;
@@ -97,6 +101,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; };
private _started: boolean;
private _remoteConnectionData: IRemoteConnectionData | null;
private readonly _disposables: DisposableStore;
@@ -112,6 +117,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
@IExtHostTunnelService extHostTunnelService: IExtHostTunnelService,
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
) {
super();
this._hostUtils = hostUtils;
this._extHostContext = extHostContext;
this._initData = initData;
@@ -164,6 +170,11 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
this._extensionPathIndex = null;
this._resolvers = Object.create(null);
this._started = false;
this._remoteConnectionData = this._initData.remote.connectionData;
}
public getRemoteConnectionData(): IRemoteConnectionData | null {
return this._remoteConnectionData;
}
public async initialize(): Promise<void> {
@@ -360,7 +371,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage);
const extensionMode = extensionDescription.isUnderDevelopment
? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development)
: ExtensionMode.Release;
: ExtensionMode.Production;
this._logService.trace(`ExtensionService#loadExtensionContext ${extensionDescription.identifier.value}`);
@@ -380,10 +391,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
get globalStoragePath() { return that._storagePath.globalValue(extensionDescription); },
asAbsolutePath(relativePath: string) { return path.join(extensionDescription.extensionLocation.fsPath, relativePath); },
get logPath() { return path.join(that._initData.logsLocation.fsPath, extensionDescription.identifier.value); },
get extensionMode() {
checkProposedApiEnabled(extensionDescription);
return extensionMode;
},
get extensionMode() { return extensionMode; },
get environmentVariableCollection() { return that._extHostTerminalService.getEnvironmentVariableCollection(extensionDescription); }
});
});
@@ -426,15 +434,44 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
// -- eager activation
private _activateOneStartupFinished(desc: IExtensionDescription, activationEvent: string): void {
this._activateById(desc.identifier, {
startup: false,
extensionId: desc.identifier,
activationEvent: activationEvent
}).then(undefined, (err) => {
this._logService.error(err);
});
}
private _activateAllStartupFinished(): void {
for (const desc of this._registry.getAllExtensionDescriptions()) {
if (desc.activationEvents) {
for (const activationEvent of desc.activationEvents) {
if (activationEvent === 'onStartupFinished') {
this._activateOneStartupFinished(desc, activationEvent);
}
}
}
}
}
// Handle "eager" activation extensions
private _handleEagerExtensions(): Promise<void> {
this._activateByEvent('*', true).then(undefined, (err) => {
const starActivation = this._activateByEvent('*', true).then(undefined, (err) => {
this._logService.error(err);
});
this._disposables.add(this._extHostWorkspace.onDidChangeWorkspace((e) => this._handleWorkspaceContainsEagerExtensions(e.added)));
const folders = this._extHostWorkspace.workspace ? this._extHostWorkspace.workspace.folders : [];
return this._handleWorkspaceContainsEagerExtensions(folders);
const workspaceContainsActivation = this._handleWorkspaceContainsEagerExtensions(folders);
const eagerExtensionsActivation = Promise.all([starActivation, workspaceContainsActivation]).then(() => { });
Promise.race([eagerExtensionsActivation, timeout(10000)]).then(() => {
this._activateAllStartupFinished();
});
return eagerExtensionsActivation;
}
private _handleWorkspaceContainsEagerExtensions(folders: ReadonlyArray<vscode.WorkspaceFolder>): Promise<void> {
@@ -764,6 +801,11 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio
return buff;
}
public async $updateRemoteConnectionData(connectionData: IRemoteConnectionData): Promise<void> {
this._remoteConnectionData = connectionData;
this._onDidChangeRemoteConnectionData.fire();
}
public abstract async $setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
}
@@ -798,7 +840,7 @@ function getTelemetryActivationEvent(extensionDescription: IExtensionDescription
export const IExtHostExtensionService = createDecorator<IExtHostExtensionService>('IExtHostExtensionService');
export interface IExtHostExtensionService extends AbstractExtHostExtensionService {
_serviceBrand: undefined;
readonly _serviceBrand: undefined;
initialize(): Promise<void>;
isActivated(extensionId: ExtensionIdentifier): boolean;
activateByIdWithErrors(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
@@ -807,4 +849,7 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic
getExtensionRegistry(): Promise<ExtensionDescriptionRegistry>;
getExtensionPathIndex(): Promise<TernarySearchTree<string, IExtensionDescription>>;
registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable;
onDidChangeRemoteConnectionData: Event<void>;
getRemoteConnectionData(): IRemoteConnectionData | null;
}

View File

@@ -9,6 +9,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
export const IExtHostInitDataService = createDecorator<IExtHostInitDataService>('IExtHostInitDataService');
export interface IExtHostInitDataService extends Readonly<IInitData> {
_serviceBrand: undefined;
readonly _serviceBrand: undefined;
}

View File

@@ -13,13 +13,19 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
import { CellKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, MainThreadDocumentsShape, INotebookEditorPropertiesChangeData, INotebookDocumentsAndEditorsDelta } 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, CellUri, diff, ICellEditOperation, ICellInsertEdit, INotebookDisplayOrder, INotebookEditData, NotebookCellsChangedEvent, NotebookCellsSplice2, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseOutputInfo, IOutputRenderResponseCellInfo } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, INotebookDisplayOrder, INotebookEditData, NotebookCellsChangedEvent, NotebookCellsSplice2, ICellDeleteEdit, notebookDocumentMetadataDefaults, NotebookCellsChangeType, NotebookDataDto, IOutputRenderRequest, IOutputRenderResponse, IOutputRenderResponseOutputInfo, IOutputRenderResponseCellInfo, IRawOutput, CellOutputKind, IProcessedOutput } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData';
import { NotImplementedProxy } from 'vs/base/common/types';
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 { generateUuid } from 'vs/base/common/uuid';
import { Cache } from './cache';
interface IObservable<T> {
proxy: T;
@@ -48,15 +54,18 @@ interface INotebookEventEmitter {
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void;
}
const addIdToOutput = (output: IRawOutput, id = generateUuid()): IProcessedOutput => output.outputKind === CellOutputKind.Rich
? ({ ...output, outputId: id }) : output;
export class ExtHostCell extends Disposable implements vscode.NotebookCell {
// private originalSource: string[];
private _outputs: any[];
private _onDidChangeOutputs = new Emitter<ISplice<vscode.CellOutput>[]>();
onDidChangeOutputs: Event<ISplice<vscode.CellOutput>[]> = this._onDidChangeOutputs.event;
private _onDidChangeOutputs = new Emitter<ISplice<IProcessedOutput>[]>();
onDidChangeOutputs: Event<ISplice<IProcessedOutput>[]> = this._onDidChangeOutputs.event;
// private _textDocument: vscode.TextDocument | undefined;
// private _initalVersion: number = -1;
private _outputMapping = new Set<vscode.CellOutput>();
private _outputMapping = new WeakMap<vscode.CellOutput, string | undefined /* output ID */>();
private _metadata: vscode.NotebookCellMetadata;
private _metadataChangeListener: IDisposable;
@@ -71,11 +80,6 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
return this._notebook;
}
get source() {
// todo@jrieken remove this
return this._documentData.getText();
}
constructor(
private readonly _notebook: ExtHostNotebookDocument,
readonly handle: number,
@@ -96,6 +100,10 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
);
this._outputs = outputs;
for (const output of this._outputs) {
this._outputMapping.set(output, output.outputId);
delete output.outputId;
}
const observableMetadata = getObservable(_metadata || {});
this._metadata = observableMetadata.proxy;
@@ -109,22 +117,33 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
}
set outputs(newOutputs: vscode.CellOutput[]) {
let diffs = diff<vscode.CellOutput>(this._outputs || [], newOutputs || [], (a) => {
let rawDiffs = diff<vscode.CellOutput>(this._outputs || [], newOutputs || [], (a) => {
return this._outputMapping.has(a);
});
diffs.forEach(diff => {
const transformedDiffs: ISplice<IProcessedOutput>[] = rawDiffs.map(diff => {
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
this._outputMapping.delete(this._outputs[i]);
}
diff.toInsert.forEach(output => {
this._outputMapping.add(output);
});
return {
deleteCount: diff.deleteCount,
start: diff.start,
toInsert: diff.toInsert.map((output): IProcessedOutput => {
if (output.outputKind === CellOutputKind.Rich) {
const uuid = generateUuid();
this._outputMapping.set(output, uuid);
return { ...output, outputId: uuid };
}
this._outputMapping.set(output, undefined);
return output;
})
};
});
this._outputs = newOutputs;
this._onDidChangeOutputs.fire(diffs);
this._onDidChangeOutputs.fire(transformedDiffs);
}
get metadata() {
@@ -228,6 +247,47 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
return this._versionId;
}
private _backupCounter = 1;
private _backup?: vscode.NotebookDocumentBackup;
private readonly _edits = new Cache<vscode.NotebookDocumentEditEvent>('notebook documents');
addEdit(item: vscode.NotebookDocumentEditEvent): number {
return this._edits.add([item]);
}
async undo(editId: number, isDirty: boolean): Promise<void> {
await this.getEdit(editId).undo();
// if (!isDirty) {
// this.disposeBackup();
// }
}
async redo(editId: number, isDirty: boolean): Promise<void> {
await this.getEdit(editId).redo();
// if (!isDirty) {
// this.disposeBackup();
// }
}
private getEdit(editId: number): vscode.NotebookDocumentEditEvent {
const edit = this._edits.get(editId, 0);
if (!edit) {
throw new Error('No edit found');
}
return edit;
}
disposeEdits(editIds: number[]): void {
for (const id of editIds) {
this._edits.delete(id);
}
}
private _disposed = false;
constructor(
@@ -236,7 +296,8 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
private _emitter: INotebookEventEmitter,
public viewType: string,
public uri: URI,
public renderingHandler: ExtHostNotebookOutputRenderingHandler
public renderingHandler: ExtHostNotebookOutputRenderingHandler,
private readonly _storagePath: URI | undefined
) {
super();
@@ -251,6 +312,24 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
this._proxy.$updateNotebookMetadata(this.viewType, this.uri, this._metadata);
}
getNewBackupUri(): URI {
if (!this._storagePath) {
throw new Error('Backup requires a valid storage path');
}
const fileName = hashPath(this.uri) + (this._backupCounter++);
return joinPath(this._storagePath, fileName);
}
updateBackup(backup: vscode.NotebookDocumentBackup): void {
this._backup?.delete();
this._backup = backup;
}
disposeBackup(): void {
this._backup?.delete();
this._backup = undefined;
}
dispose() {
this._disposed = true;
super.dispose();
@@ -263,8 +342,10 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
accpetModelChanged(event: NotebookCellsChangedEvent): void {
this._versionId = event.versionId;
if (event.kind === NotebookCellsChangeType.ModelChange) {
this.$spliceNotebookCells(event.changes);
if (event.kind === NotebookCellsChangeType.Initialize) {
this.$spliceNotebookCells(event.changes, true);
} if (event.kind === NotebookCellsChangeType.ModelChange) {
this.$spliceNotebookCells(event.changes, false);
} else if (event.kind === NotebookCellsChangeType.Move) {
this.$moveCell(event.index, event.newIdx);
} else if (event.kind === NotebookCellsChangeType.CellClearOutput) {
@@ -276,7 +357,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
}
}
private $spliceNotebookCells(splices: NotebookCellsSplice2[]): void {
private $spliceNotebookCells(splices: NotebookCellsSplice2[], initialization: boolean): void {
if (this._disposed) {
return;
}
@@ -312,21 +393,24 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
}
this.cells.splice(splice[0], splice[1], ...newCells);
const deletedItems = this.cells.splice(splice[0], splice[1], ...newCells);
const event: vscode.NotebookCellsChangeData = {
start: splice[0],
deletedCount: splice[1],
deletedItems,
items: newCells
};
contentChangeEvents.push(event);
});
this._emitter.emitModelChange({
document: this,
changes: contentChangeEvents
});
if (!initialization) {
this._emitter.emitModelChange({
document: this,
changes: contentChangeEvents
});
}
}
private $moveCell(index: number, newIdx: number): void {
@@ -335,10 +419,12 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
const changes: vscode.NotebookCellsChangeData[] = [{
start: index,
deletedCount: 1,
deletedItems: cells,
items: []
}, {
start: newIdx,
deletedCount: 0,
deletedItems: [],
items: cells
}];
this._emitter.emitModelChange({
@@ -373,7 +459,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
this._emitter.emitCellLanguageChange(event);
}
async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<vscode.CellOutput>[]) {
async eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<IProcessedOutput>[]) {
let renderers = new Set<number>();
let outputDtos: NotebookCellOutputsSplice[] = diffs.map(diff => {
let outputs = diff.toInsert;
@@ -445,8 +531,8 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE
source: sourceArr,
language,
cellKind: type,
outputs: outputs,
metadata
outputs: outputs.map(o => addIdToOutput(o)),
metadata,
};
this._collectedEdits.push({
@@ -467,6 +553,30 @@ export class NotebookEditorCellEditBuilder implements vscode.NotebookEditorCellE
}
}
class ExtHostWebviewComm extends Disposable {
onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
constructor(
readonly id: string,
public uri: URI,
private _proxy: MainThreadNotebookShape,
private _onDidReceiveMessage: Emitter<any>,
private _webviewInitData: WebviewInitData,
public document: ExtHostNotebookDocument,
) {
super();
}
async postMessage(message: any): Promise<boolean> {
return this._proxy.$postMessage(this.document.handle, message);
}
asWebviewUri(localResource: vscode.Uri): vscode.Uri {
return asWebviewUri(this._webviewInitData, this.id, localResource);
}
}
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
private _viewColumn: vscode.ViewColumn | undefined;
@@ -500,6 +610,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
private _onDidDispose = new Emitter<void>();
readonly onDidDispose: Event<void> = this._onDidDispose.event;
private _onDidReceiveMessage = new Emitter<any>();
onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
constructor(
@@ -507,8 +618,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
readonly id: string,
public uri: URI,
private _proxy: MainThreadNotebookShape,
private _onDidReceiveMessage: Emitter<any>,
private _webviewInitData: WebviewInitData,
private _webComm: ExtHostWebviewComm,
public document: ExtHostNotebookDocument,
private _documentsAndEditors: ExtHostDocumentsAndEditors
) {
@@ -517,7 +627,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
for (const documentData of documents) {
let data = CellUri.parse(documentData.document.uri);
if (data) {
if (this.document.uri.toString() === data.notebook.toString()) {
if (this.document.uri.fsPath === data.notebook.fsPath) {
document.attachCellTextDocument(documentData);
}
}
@@ -528,12 +638,16 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
for (const documentData of documents) {
let data = CellUri.parse(documentData.document.uri);
if (data) {
if (this.document.uri.toString() === data.notebook.toString()) {
if (this.document.uri.fsPath === data.notebook.fsPath) {
document.detachCellTextDocument(documentData);
}
}
}
}));
this._register(this._webComm.onDidReceiveMessage(e => {
this._onDidReceiveMessage.fire(e);
}));
}
edit(callback: (editBuilder: NotebookEditorCellEditBuilder) => void): Thenable<boolean> {
@@ -593,11 +707,11 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
}
async postMessage(message: any): Promise<boolean> {
return this._proxy.$postMessage(this.document.handle, message);
return this._webComm.postMessage(message);
}
asWebviewUri(localResource: vscode.Uri): vscode.Uri {
return asWebviewUri(this._webviewInitData, this.id, localResource);
return this._webComm.asWebviewUri(localResource);
}
dispose() {
this._onDidDispose.fire();
@@ -618,16 +732,16 @@ export class ExtHostNotebookOutputRenderer {
}
matches(mimeType: string): boolean {
if (this.filter.subTypes) {
if (this.filter.subTypes.indexOf(mimeType) >= 0) {
if (this.filter.mimeTypes) {
if (this.filter.mimeTypes.indexOf(mimeType) >= 0) {
return true;
}
}
return false;
}
render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, mimeType: string): string {
let html = this.renderer.render(document, output, mimeType);
render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, outputId: string, mimeType: string): string {
let html = this.renderer.render(document, { output, outputId, mimeType });
return html;
}
@@ -644,7 +758,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
private readonly _notebookKernels = new Map<string, { readonly kernel: vscode.NotebookKernel, readonly extension: IExtensionDescription; }>();
private readonly _documents = new Map<string, ExtHostNotebookDocument>();
private readonly _unInitializedDocuments = new Map<string, ExtHostNotebookDocument>();
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any>; }>();
private readonly _editors = new Map<string, { editor: ExtHostNotebookEditor }>();
private readonly _webviewComm = new Map<string, { comm: ExtHostWebviewComm, onDidReceiveMessage: Emitter<any> }>();
private readonly _notebookOutputRenderers = new Map<string, ExtHostNotebookOutputRenderer>();
private readonly _onDidChangeNotebookCells = new Emitter<vscode.NotebookCellsChangeEvent>();
readonly onDidChangeNotebookCells = this._onDidChangeNotebookCells.event;
@@ -661,18 +776,16 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
return this._outputDisplayOrder;
}
private _activeNotebookDocument: ExtHostNotebookDocument | undefined;
get activeNotebookDocument() {
return this._activeNotebookDocument;
}
private _activeNotebookEditor: ExtHostNotebookEditor | undefined;
get activeNotebookEditor() {
return this._activeNotebookEditor;
}
get notebookDocuments() {
return [...this._documents.values()];
}
private _onDidOpenNotebookDocument = new Emitter<vscode.NotebookDocument>();
onDidOpenNotebookDocument: Event<vscode.NotebookDocument> = this._onDidOpenNotebookDocument.event;
private _onDidCloseNotebookDocument = new Emitter<vscode.NotebookDocument>();
@@ -681,7 +794,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
private _onDidChangeVisibleNotebookEditors = new Emitter<vscode.NotebookEditor[]>();
onDidChangeVisibleNotebookEditors = this._onDidChangeVisibleNotebookEditors.event;
constructor(mainContext: IMainContext, commands: ExtHostCommands, private _documentsAndEditors: ExtHostDocumentsAndEditors, private readonly _webviewInitData: WebviewInitData) {
constructor(
mainContext: IMainContext,
commands: ExtHostCommands,
private _documentsAndEditors: ExtHostDocumentsAndEditors,
private readonly _webviewInitData: WebviewInitData,
private readonly _extensionStoragePaths?: IExtensionStoragePaths,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadNotebook);
commands.registerArgumentProcessor({
@@ -736,13 +855,14 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
const renderer = this._notebookOutputRenderers.get(id)!;
const cellsResponse: IOutputRenderResponseCellInfo<UriComponents>[] = request.items.map(cellInfo => {
const cell = document.getCell2(cellInfo.key);
const cell = document.getCell2(cellInfo.key)!;
const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => {
return {
index: output.index,
outputId: output.outputId,
mimeType: output.mimeType,
handlerId: id,
transformedOutput: renderer.render(document, cell!.outputs[output.index] as vscode.CellDisplayOutput, output.mimeType)
transformedOutput: renderer.render(document, cell.outputs[output.index] as vscode.CellDisplayOutput, output.outputId, output.mimeType)
};
});
@@ -774,9 +894,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
const outputResponse: IOutputRenderResponseOutputInfo[] = cellInfo.outputs.map(output => {
return {
index: output.index,
outputId: output.outputId,
mimeType: output.mimeType,
handlerId: id,
transformedOutput: renderer.render(document, output.output! as vscode.CellDisplayOutput, output.mimeType)
transformedOutput: renderer.render(document, output.output! as vscode.CellDisplayOutput, output.outputId, output.mimeType)
};
});
@@ -817,10 +938,26 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
this._notebookContentProviders.set(viewType, { extension, provider });
const listener = provider.onDidChangeNotebook
? provider.onDidChangeNotebook(e => this._proxy.$onNotebookChange(viewType, e.document.uri))
? provider.onDidChangeNotebook(e => {
const document = this._documents.get(URI.revive(e.document.uri).toString());
if (!document) {
throw new Error(`Notebook document ${e.document.uri.toString()} not found`);
}
if (isEditEvent(e)) {
const editId = document.addEdit(e);
this._proxy.$onDidEdit(e.document.uri, viewType, editId, e.label);
} else {
this._proxy.$onContentChange(e.document.uri, viewType);
}
})
: Disposable.None;
this._proxy.$registerNotebookProvider({ id: extension.identifier, location: extension.extensionLocation }, viewType, provider.kernel ? { id: viewType, label: provider.kernel.label, extensionLocation: extension.extensionLocation, preloads: provider.kernel.preloads } : undefined);
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);
return new extHostTypes.Disposable(() => {
listener.dispose();
this._notebookContentProviders.delete(viewType);
@@ -843,11 +980,16 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
});
}
async $resolveNotebookData(viewType: string, uri: UriComponents): Promise<NotebookDataDto | undefined> {
async $resolveNotebookData(viewType: string, uri: UriComponents, backupId?: string): Promise<NotebookDataDto | undefined> {
const provider = this._notebookContentProviders.get(viewType);
const revivedUri = URI.revive(uri);
if (provider) {
let storageRoot: URI | undefined;
if (this._extensionStoragePaths) {
storageRoot = URI.file(this._extensionStoragePaths.workspaceValue(provider.extension) ?? this._extensionStoragePaths.globalValue(provider.extension));
}
let document = this._documents.get(URI.revive(uri).toString());
if (!document) {
@@ -862,18 +1004,21 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
that._onDidChangeCellLanguage.fire(event);
}
}, viewType, revivedUri, this);
}, viewType, revivedUri, this, storageRoot);
this._unInitializedDocuments.set(revivedUri.toString(), document);
}
const rawCells = await provider.provider.openNotebook(URI.revive(uri));
const rawCells = await provider.provider.openNotebook(URI.revive(uri), { backupId });
const dto = {
metadata: {
...notebookDocumentMetadataDefaults,
...rawCells.metadata
},
languages: rawCells.languages,
cells: rawCells.cells,
cells: rawCells.cells.map(cell => ({
...cell,
outputs: cell.outputs.map(o => addIdToOutput(o))
})),
};
return dto;
@@ -882,6 +1027,30 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
return undefined; // {{SQL CARBON EDIT}}
}
async $resolveNotebookEditor(viewType: string, uri: UriComponents, editorId: string): Promise<void> {
const provider = this._notebookContentProviders.get(viewType);
const revivedUri = URI.revive(uri);
const document = this._documents.get(revivedUri.toString());
if (!document || !provider) {
return;
}
if (!provider.provider.resolveNotebook) {
return;
}
let webComm = this._webviewComm.get(editorId)?.comm;
if (webComm) {
await provider.provider.resolveNotebook(document, webComm);
} else {
const onDidReceiveMessage = new Emitter<any>();
webComm = new ExtHostWebviewComm(editorId, revivedUri, this._proxy, onDidReceiveMessage, this._webviewInitData, document);
this._webviewComm.set(editorId, { comm: webComm, onDidReceiveMessage });
await provider.provider.resolveNotebook(document, webComm);
}
}
async $executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined, useAttachedKernel: boolean, token: CancellationToken): Promise<void> {
let document = this._documents.get(URI.revive(uri).toString());
@@ -963,6 +1132,39 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
return false;
}
async $undoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
const document = this._documents.get(URI.revive(uri).toString());
if (!document) {
return;
}
document.undo(editId, isDirty);
}
async $redoNotebook(viewType: string, uri: UriComponents, editId: number, isDirty: boolean): Promise<void> {
const document = this._documents.get(URI.revive(uri).toString());
if (!document) {
return;
}
document.redo(editId, isDirty);
}
async $backup(viewType: string, uri: UriComponents, cancellation: CancellationToken): Promise<string | undefined> {
const document = this._documents.get(URI.revive(uri).toString());
const provider = this._notebookContentProviders.get(viewType);
if (document && provider && provider.provider.backupNotebook) {
const backup = await provider.provider.backupNotebook(document, { destination: document.getNewBackupUri() }, cancellation);
document.updateBackup(backup);
return backup.id;
}
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
}
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void {
this._outputDisplayOrder = displayOrder;
}
@@ -970,7 +1172,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
// TODO: remove document - editor one on one mapping
private _getEditorFromURI(uriComponents: UriComponents) {
const uriStr = URI.revive(uriComponents).toString();
let editor: { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any>; } | undefined;
let editor: { editor: ExtHostNotebookEditor } | undefined;
this._editors.forEach(e => {
if (e.editor.uri.toString() === uriStr) {
editor = e;
@@ -981,10 +1183,10 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
$onDidReceiveMessage(editorId: string, message: any): void {
let editor = this._editors.get(editorId);
let messageEmitter = this._webviewComm.get(editorId)?.onDidReceiveMessage;
if (editor) {
editor.onDidReceiveMessage.fire(message);
if (messageEmitter) {
messageEmitter.fire(message);
}
}
@@ -1023,16 +1225,21 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
private _createExtHostEditor(document: ExtHostNotebookDocument, editorId: string, selections: number[]) {
const onDidReceiveMessage = new Emitter<any>();
const revivedUri = document.uri;
let webComm = this._webviewComm.get(editorId)?.comm;
if (!webComm) {
const onDidReceiveMessage = new Emitter<any>();
webComm = new ExtHostWebviewComm(editorId, revivedUri, this._proxy, onDidReceiveMessage, this._webviewInitData, document);
this._webviewComm.set(editorId, { comm: webComm!, onDidReceiveMessage });
}
let editor = new ExtHostNotebookEditor(
document.viewType,
editorId,
revivedUri,
this._proxy,
onDidReceiveMessage,
this._webviewInitData,
webComm,
document,
this._documentsAndEditors
);
@@ -1048,7 +1255,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
this._editors.get(editorId)?.editor.dispose();
this._editors.set(editorId, { editor, onDidReceiveMessage });
this._editors.set(editorId, { editor });
}
async $acceptDocumentAndEditorsDelta(delta: INotebookDocumentsAndEditorsDelta) {
@@ -1069,7 +1276,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
[...this._editors.values()].forEach((e) => {
if (e.editor.uri.toString() === revivedUriStr) {
e.editor.dispose();
e.onDidReceiveMessage.dispose();
this._editors.delete(e.editor.id);
editorChanged = true;
}
@@ -1082,8 +1288,15 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
const revivedUri = URI.revive(modelData.uri);
const revivedUriStr = revivedUri.toString();
const viewType = modelData.viewType;
const entry = this._notebookContentProviders.get(viewType);
let storageRoot: URI | undefined;
if (entry && this._extensionStoragePaths) {
storageRoot = URI.file(this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension));
}
if (!this._documents.has(revivedUriStr)) {
const that = this;
let document = this._unInitializedDocuments.get(revivedUriStr) ?? new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, {
emitModelChange(event: vscode.NotebookCellsChangeEvent): void {
that._onDidChangeNotebookCells.fire(event);
@@ -1094,7 +1307,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
emitCellLanguageChange(event: vscode.NotebookCellLanguageChangeEvent): void {
that._onDidChangeCellLanguage.fire(event);
}
}, viewType, revivedUri, this);
}, viewType, revivedUri, this, storageRoot);
this._unInitializedDocuments.delete(revivedUriStr);
if (modelData.metadata) {
@@ -1105,7 +1318,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
document.accpetModelChanged({
kind: NotebookCellsChangeType.ModelChange,
kind: NotebookCellsChangeType.Initialize,
versionId: modelData.versionId,
changes: [[
0,
@@ -1145,7 +1358,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
});
}
const removedEditors: { editor: ExtHostNotebookEditor, onDidReceiveMessage: Emitter<any>; }[] = [];
const removedEditors: { editor: ExtHostNotebookEditor }[] = [];
if (delta.removedEditors) {
delta.removedEditors.forEach(editorid => {
@@ -1157,7 +1370,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
if (this.activeNotebookEditor?.id === editor.editor.id) {
this._activeNotebookEditor = undefined;
this._activeNotebookDocument = undefined;
}
removedEditors.push(editor);
@@ -1168,12 +1380,11 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
if (editorChanged) {
removedEditors.forEach(e => {
e.editor.dispose();
e.onDidReceiveMessage.dispose();
});
}
if (delta.visibleEditors) {
this.visibleNotebookEditors = delta.visibleEditors.map(id => this._editors.get(id)?.editor).filter(editor => !!editor) as ExtHostNotebookEditor[];
this.visibleNotebookEditors = delta.visibleEditors.map(id => this._editors.get(id)!.editor).filter(editor => !!editor) as ExtHostNotebookEditor[];
const visibleEditorsSet = new Set<string>();
this.visibleNotebookEditors.forEach(editor => visibleEditorsSet.add(editor.id));
@@ -1190,19 +1401,32 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
if (delta.newActiveEditor) {
this._activeNotebookEditor = this._editors.get(delta.newActiveEditor)?.editor;
this._activeNotebookEditor?._acceptActive(true);
this._activeNotebookDocument = this._activeNotebookEditor ? this._documents.get(this._activeNotebookEditor!.uri.toString()) : undefined;
[...this._editors.values()].forEach((e) => {
if (e.editor !== this.activeNotebookEditor) {
e.editor._acceptActive(false);
}
});
} else {
// clear active notebook as current active editor is non-notebook editor
this._activeNotebookEditor = undefined;
this._activeNotebookDocument = undefined;
[...this._editors.values()].forEach((e) => {
e.editor._acceptActive(false);
});
}
this._onDidChangeActiveNotebookEditor.fire(this._activeNotebookEditor);
}
[...this._editors.values()].forEach((e) => {
if (e.editor !== this.activeNotebookEditor) {
e.editor._acceptActive(false);
}
});
}
}
function hashPath(resource: URI): string {
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
return hash(str) + '';
}
function isEditEvent(e: vscode.NotebookDocumentEditEvent | vscode.NotebookDocumentContentChangeEvent): e is vscode.NotebookDocumentEditEvent {
return typeof (e as vscode.NotebookDocumentEditEvent).undo === 'function'
&& typeof (e as vscode.NotebookDocumentEditEvent).redo === 'function';
}

View File

@@ -9,7 +9,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
export const IExtHostRpcService = createDecorator<IExtHostRpcService>('IExtHostRpcService');
export interface IExtHostRpcService extends IRPCProtocol {
_serviceBrand: undefined;
readonly _serviceBrand: undefined;
}
export class ExtHostRpcService implements IExtHostRpcService {

View File

@@ -9,7 +9,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
export const IExtensionStoragePaths = createDecorator<IExtensionStoragePaths>('IExtensionStoragePaths');
export interface IExtensionStoragePaths {
_serviceBrand: undefined;
readonly _serviceBrand: undefined;
whenReady: Promise<any>;
workspaceValue(extension: IExtensionDescription): string | undefined;
globalValue(extension: IExtensionDescription): string;

View File

@@ -512,12 +512,10 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
public async $onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): Promise<void> {
const execution = await this.getTaskExecution(value.id);
if (execution) {
this._onDidTaskProcessStarted.fire({
execution: execution,
processId: value.processId
});
}
this._onDidTaskProcessStarted.fire({
execution: execution,
processId: value.processId
});
}
public get onDidEndTaskProcess(): Event<vscode.TaskProcessEndEvent> {
@@ -526,12 +524,10 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
public async $onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): Promise<void> {
const execution = await this.getTaskExecution(value.id);
if (execution) {
this._onDidTaskProcessEnded.fire({
execution: execution,
exitCode: value.exitCode
});
}
this._onDidTaskProcessEnded.fire({
execution: execution,
exitCode: value.exitCode
});
}
protected abstract provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription };
@@ -622,7 +618,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
protected async getTaskExecution(execution: tasks.TaskExecutionDTO | string, task?: vscode.Task): Promise<TaskExecutionImpl> {
if (typeof execution === 'string') {
const taskExecution = this._taskExecutions.get(execution);
const taskExecution = this._taskExecutionPromises.get(execution);
if (!taskExecution) {
throw new Error('Unexpected: The specified task is missing an execution');
}
@@ -643,9 +639,11 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape {
});
this._taskExecutionPromises.set(execution.id, createdResult);
return createdResult.then(result => {
this._taskExecutions.set(execution.id, result);
return result;
return createdResult.then(executionCreatedResult => {
this._taskExecutions.set(execution.id, executionCreatedResult);
return executionCreatedResult;
}, rejected => {
return Promise.reject(rejected);
});
}
@@ -708,7 +706,7 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise<vscode.TaskExecution> {
const dto = TaskDTO.from(task, extension);
if (dto === undefined) {
return Promise.reject(new Error('Task is not valid'));
throw new Error('Task is not valid');
}
// If this task is a custom execution, then we need to save it away
@@ -720,7 +718,10 @@ export class WorkerExtHostTask extends ExtHostTaskBase {
throw new Error('Not implemented');
}
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
// Always get the task execution first to prevent timing issues when retrieving it later
const execution = await this.getTaskExecution(await this._proxy.$getTaskExecution(dto), task);
this._proxy.$executeTask(dto).catch(error => { throw new Error(error); });
return execution;
}
protected provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise<void>[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription } {

View File

@@ -9,7 +9,7 @@ import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShap
import { ExtHostConfigProvider } from 'vs/workbench/api/common/extHostConfiguration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY } from 'vs/workbench/contrib/terminal/common/terminal';
import { ITerminalChildProcess, ITerminalDimensions, EXT_HOST_CREATION_DELAY, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { timeout } from 'vs/base/common/async';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
@@ -17,10 +17,11 @@ import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { localize } from 'vs/nls';
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
_serviceBrand: undefined;
readonly _serviceBrand: undefined;
activeTerminal: vscode.Terminal | undefined;
terminals: vscode.Terminal[];
@@ -243,6 +244,10 @@ export class ExtHostPseudoterminal implements ITerminalChildProcess {
constructor(private readonly _pty: vscode.Pseudoterminal) { }
async start(): Promise<undefined> {
return undefined;
}
shutdown(): void {
this._pty.close();
}
@@ -332,7 +337,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
public abstract createTerminalFromOptions(options: vscode.TerminalOptions): vscode.Terminal;
public abstract getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
public abstract getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void>;
public abstract $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined>;
public abstract $getAvailableShells(): Promise<IShellDefinitionDto[]>;
public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
@@ -454,20 +459,17 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
}
}
public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<void> {
public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise<ITerminalLaunchError | undefined> {
// Make sure the ExtHostTerminal exists so onDidOpenTerminal has fired before we call
// Pseudoterminal.start
const terminal = await this._getTerminalByIdEventually(id);
if (!terminal) {
return;
return { message: localize('launchFail.idMissingOnExtHost', "Could not find the terminal with id {0} on the extension host", id) };
}
// Wait for onDidOpenTerminal to fire
let openPromise: Promise<void>;
if (terminal.isOpen) {
openPromise = Promise.resolve();
} else {
openPromise = new Promise<void>(r => {
if (!terminal.isOpen) {
await new Promise<void>(r => {
// Ensure open is called after onDidOpenTerminal
const listener = this.onDidOpenTerminal(async e => {
if (e === terminal) {
@@ -477,7 +479,6 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
});
});
}
await openPromise;
if (this._terminalProcesses[id]) {
(this._terminalProcesses[id] as ExtHostPseudoterminal).startSendingEvents(initialDimensions);
@@ -486,6 +487,7 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
this._extensionTerminalAwaitingStart[id] = { initialDimensions };
}
return undefined;
}
protected _setupExtHostProcessListeners(id: number, p: ITerminalChildProcess): IDisposable {
@@ -721,7 +723,7 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
throw new Error('Not implemented');
}
public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
public $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined> {
throw new Error('Not implemented');
}

View File

@@ -54,7 +54,7 @@ export class ExtHostEditors implements ExtHostEditorsShape {
showTextDocument(document: vscode.TextDocument, column: vscode.ViewColumn, preserveFocus: boolean): Promise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, options: { column: vscode.ViewColumn, preserveFocus: boolean, pinned: boolean }): Promise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions | undefined, preserveFocus?: boolean): Promise<vscode.TextEditor>;
showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions | undefined, preserveFocus?: boolean): Promise<vscode.TextEditor> {
async showTextDocument(document: vscode.TextDocument, columnOrOptions: vscode.ViewColumn | vscode.TextDocumentShowOptions | undefined, preserveFocus?: boolean): Promise<vscode.TextEditor> {
let options: ITextDocumentShowOptions;
if (typeof columnOrOptions === 'number') {
options = {
@@ -74,14 +74,18 @@ export class ExtHostEditors implements ExtHostEditorsShape {
};
}
return this._proxy.$tryShowTextDocument(document.uri, options).then(id => {
const editor = id && this._extHostDocumentsAndEditors.getEditor(id);
if (editor) {
return editor;
} else {
throw new Error(`Failed to show text document ${document.uri.toString()}, should show in editor #${id}`);
}
});
const editorId = await this._proxy.$tryShowTextDocument(document.uri, options);
const editor = editorId && this._extHostDocumentsAndEditors.getEditor(editorId);
if (editor) {
return editor;
}
// we have no editor... having an id means that we had an editor
// on the main side and that it isn't the current editor anymore...
if (editorId) {
throw new Error(`Could NOT open editor for "${document.uri.toString()}" because another editor opened in the meantime.`);
} else {
throw new Error(`Could NOT open editor for "${document.uri.toString()}".`);
}
}
createTextEditorDecorationType(options: vscode.DecorationRenderOptions): vscode.TextEditorDecorationType {

View File

@@ -22,7 +22,7 @@ export interface IExtHostTimeline extends ExtHostTimelineShape {
export const IExtHostTimeline = createDecorator<IExtHostTimeline>('IExtHostTimeline');
export class ExtHostTimeline implements IExtHostTimeline {
_serviceBrand: undefined;
declare readonly _serviceBrand: undefined;
private _proxy: MainThreadTimelineShape;

View File

@@ -41,7 +41,7 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape {
export const IExtHostTunnelService = createDecorator<IExtHostTunnelService>('IExtHostTunnelService');
export class ExtHostTunnelService implements IExtHostTunnelService {
_serviceBrand: undefined;
declare readonly _serviceBrand: undefined;
onDidChangeTunnels: vscode.Event<void> = (new Emitter<void>()).event;
private readonly _proxy: MainThreadTunnelServiceShape;

View File

@@ -1164,15 +1164,20 @@ export namespace FoldingRangeKind {
}
}
export namespace TextEditorOptions {
export interface TextEditorOpenOptions extends vscode.TextDocumentShowOptions {
background?: boolean;
}
export function from(options?: vscode.TextDocumentShowOptions): ITextEditorOptions | undefined {
export namespace TextEditorOpenOptions {
export function from(options?: TextEditorOpenOptions): ITextEditorOptions | undefined {
if (options) {
return {
pinned: typeof options.preview === 'boolean' ? !options.preview : undefined,
inactive: options.background,
preserveFocus: options.preserveFocus,
selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined
} as ITextEditorOptions;
selection: typeof options.selection === 'object' ? Range.from(options.selection) : undefined,
};
}
return undefined;

View File

@@ -2750,7 +2750,7 @@ export enum ExtensionMode {
* The extension is installed normally (for example, from the marketplace
* or VSIX) in VS Code.
*/
Release = 1,
Production = 1,
/**
* The extension is running from an `--extensionDevelopmentPath` provided

View File

@@ -8,13 +8,13 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
import { URI, UriComponents } from 'vs/base/common/uri';
export interface IURITransformerService extends IURITransformer {
_serviceBrand: undefined;
readonly _serviceBrand: undefined;
}
export const IURITransformerService = createDecorator<IURITransformerService>('IURITransformerService');
export class URITransformerService implements IURITransformerService {
_serviceBrand: undefined;
declare readonly _serviceBrand: undefined;
transformIncoming: (uri: UriComponents) => UriComponents;
transformOutgoing: (uri: UriComponents) => UriComponents;

View File

@@ -273,7 +273,7 @@ class CustomDocumentStoreEntry {
constructor(
public readonly document: vscode.CustomDocument,
private readonly _storagePath: string,
private readonly _storagePath: URI | undefined,
) { }
private readonly _edits = new Cache<vscode.CustomDocumentEditEvent>('custom documents');
@@ -305,7 +305,11 @@ class CustomDocumentStoreEntry {
}
getNewBackupUri(): URI {
return joinPath(URI.file(this._storagePath), hashPath(this.document.uri) + (this._backupCounter++));
if (!this._storagePath) {
throw new Error('Backup requires a valid storage path');
}
const fileName = hashPath(this.document.uri) + (this._backupCounter++);
return joinPath(this._storagePath, fileName);
}
updateBackup(backup: vscode.CustomDocumentBackup): void {
@@ -334,7 +338,7 @@ class CustomDocumentStore {
return this._documents.get(this.key(viewType, resource));
}
public add(viewType: string, document: vscode.CustomDocument, storagePath: string): CustomDocumentStoreEntry {
public add(viewType: string, document: vscode.CustomDocument, storagePath: URI | undefined): CustomDocumentStoreEntry {
const key = this.key(viewType, document.uri);
if (this._documents.has(key)) {
throw new Error(`Document already exists for viewType:${viewType} resource:${document.uri}`);
@@ -592,8 +596,11 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
const revivedResource = URI.revive(resource);
const document = await entry.provider.openCustomDocument(revivedResource, { backupId }, cancellation);
const storageRoot = this._extensionStoragePaths?.workspaceValue(entry.extension) ?? this._extensionStoragePaths?.globalValue(entry.extension);
this._documents.add(viewType, document, storageRoot!);
let storageRoot: URI | undefined;
if (this.supportEditing(entry.provider) && this._extensionStoragePaths) {
storageRoot = URI.file(this._extensionStoragePaths.workspaceValue(entry.extension) ?? this._extensionStoragePaths.globalValue(entry.extension));
}
this._documents.add(viewType, document, storageRoot);
return { editable: this.supportEditing(entry.provider) };
}

View File

@@ -71,6 +71,7 @@ namespace schema {
switch (menuId) {
case MenuId.StatusBarWindowIndicatorMenu:
case MenuId.MenubarWebNavigationMenu:
case MenuId.NotebookCellTitle:
return true;
}
return false;

View File

@@ -88,6 +88,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
const configProvider = await this._configurationService.getConfigProvider();
const shell = this._terminalService.getDefaultShell(true, configProvider);
let cwdForPrepareCommand: string | undefined;
if (needNewTerminal || !this._integratedTerminalInstance) {
@@ -97,9 +98,9 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
cwd: args.cwd,
name: args.title || nls.localize('debug.terminal.title', "debuggee"),
};
// @ts-ignore
delete args.cwd;
this._integratedTerminalInstance = this._terminalService.createTerminalFromOptions(options);
} else {
cwdForPrepareCommand = args.cwd;
}
const terminal = this._integratedTerminalInstance;
@@ -107,7 +108,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
terminal.show();
const shellProcessId = await this._integratedTerminalInstance.processId;
const command = prepareCommand(args, shell);
const command = prepareCommand(shell, args.args, cwdForPrepareCommand, args.env);
terminal.sendText(command, true);
return shellProcessId;
@@ -120,7 +121,6 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase {
}
protected createVariableResolver(folders: vscode.WorkspaceFolder[], editorService: ExtHostDocumentsAndEditors, configurationService: ExtHostConfigProvider): AbstractVariableResolverService {
return new ExtHostVariableResolverService(folders, editorService, configurationService, process.env as env.IProcessEnvironment);
return new ExtHostVariableResolverService(folders, undefined, configurationService, process.env as env.IProcessEnvironment);
}
}

View File

@@ -53,7 +53,14 @@ export class ExtHostTask extends ExtHostTaskBase {
const tTask = (task as types.Task);
// We have a preserved ID. So the task didn't change.
if (tTask._id !== undefined) {
return this._proxy.$executeTask(TaskHandleDTO.from(tTask)).then(value => this.getTaskExecution(value, task));
// Always get the task execution first to prevent timing issues when retrieving it later
const executionDTO = await this._proxy.$getTaskExecution(TaskHandleDTO.from(tTask));
if (executionDTO.task === undefined) {
throw new Error('Task from execution DTO is undefined');
}
const execution = await this.getTaskExecution(executionDTO, task);
this._proxy.$executeTask(executionDTO.task).catch(error => { throw new Error(error); });
return execution;
} else {
const dto = TaskDTO.from(task, extension);
if (dto === undefined) {
@@ -66,8 +73,10 @@ export class ExtHostTask extends ExtHostTaskBase {
if (CustomExecutionDTO.is(dto.execution)) {
await this.addCustomExecution(dto, task, false);
}
return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task));
// Always get the task execution first to prevent timing issues when retrieving it later
const execution = await this.getTaskExecution(await this._proxy.$getTaskExecution(dto), task);
this._proxy.$executeTask(dto).catch(error => { throw new Error(error); });
return execution;
}
}

View File

@@ -12,7 +12,7 @@ import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/termi
import { IShellLaunchConfigDto, IShellDefinitionDto, IShellAndArgsDto } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration, ExtHostConfigProvider, IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ILogService } from 'vs/platform/log/common/log';
import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal';
import { IShellLaunchConfig, ITerminalEnvironment, ITerminalLaunchError } from 'vs/workbench/contrib/terminal/common/terminal';
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
@@ -129,7 +129,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider, process.env as platform.IProcessEnvironment);
}
public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<ITerminalLaunchError | undefined> {
const shellLaunchConfig: IShellLaunchConfig = {
name: shellLaunchConfigDto.name,
executable: shellLaunchConfigDto.executable,
@@ -209,7 +209,15 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
// 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;
this._setupExtHostProcessListeners(id, new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, enableConpty, this._logService));
const terminalProcess = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, enableConpty, this._logService);
this._setupExtHostProcessListeners(id, terminalProcess);
const error = await terminalProcess.start();
if (error) {
// TODO: Teardown?
return error;
}
return undefined;
}
public $getAvailableShells(): Promise<IShellDefinitionDto[]> {

View File

@@ -181,7 +181,7 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe
connections.filter((connection => socketMap[connection.socket])).forEach(({ socket, ip, port }) => {
const command = processMap[socketMap[socket].pid].cmd;
if (!command.match('.*\.vscode\-server\-[a-zA-Z]+\/bin.*') && (command.indexOf('out/vs/server/main.js') === -1)) {
if (!command.match(/.*\.vscode-server-[a-zA-Z]+\/bin.*/) && (command.indexOf('out/vs/server/main.js') === -1)) {
ports.push({ host: ip, port, detail: processMap[socketMap[socket].pid].cmd });
}
});

View File

@@ -11,7 +11,7 @@ import { UriComponents } from 'vs/base/common/uri';
export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape {
_serviceBrand: undefined;
declare readonly _serviceBrand: undefined;
private readonly _proxy: MainThreadLogShape;
private readonly _logFile: UriComponents;