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;
}