Merge from vscode 073a24de05773f2261f89172987002dc0ae2f1cd (#9711)

This commit is contained in:
Anthony Dresser
2020-03-24 00:24:15 -07:00
committed by GitHub
parent 29741d684e
commit 89ef1b0c2e
226 changed files with 6161 additions and 3288 deletions

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable } 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,13 +12,141 @@ 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';
interface AuthDependent {
providerId: string;
label: string;
scopes: string[];
scopeDescriptions?: string;
}
const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [
{
providerId: 'microsoft',
label: 'Settings sync',
scopes: ['https://management.core.windows.net/.default', 'offline_access'],
scopeDescriptions: 'Read user email'
}
];
export class MainThreadAuthenticationProvider extends Disposable {
private _sessionMenuItems = new Map<string, IDisposable[]>();
private _sessionIds: string[] = [];
export class MainThreadAuthenticationProvider {
constructor(
private readonly _proxy: ExtHostAuthenticationShape,
public readonly id: string,
public readonly displayName: string
) { }
public readonly displayName: string,
public readonly dependents: AuthDependent[]
) {
super();
if (!dependents.length) {
return;
}
this.registerCommandsAndContextMenuItems();
}
private setPermissionsForAccount(quickInputService: IQuickInputService, doLogin?: boolean) {
const quickPick = quickInputService.createQuickPick();
quickPick.canSelectMany = true;
const items = this.dependents.map(dependent => {
return {
label: dependent.label,
description: dependent.scopeDescriptions,
picked: true,
scopes: dependent.scopes
};
});
quickPick.items = items;
// TODO read from storage and filter is not doLogin
quickPick.selectedItems = items;
quickPick.title = nls.localize('signInTo', "Sign in to {0}", this.displayName);
quickPick.placeholder = nls.localize('accountPermissions', "Choose what features and extensions to authorize to use this account");
quickPick.onDidAccept(() => {
const scopes = quickPick.selectedItems.reduce((previous, current) => previous.concat((current as any).scopes), []);
if (scopes.length && doLogin) {
this.login(scopes);
}
quickPick.dispose();
});
quickPick.onDidHide(() => {
quickPick.dispose();
});
quickPick.show();
}
private registerCommandsAndContextMenuItems(): void {
this._register(CommandsRegistry.registerCommand({
id: `signIn${this.id}`,
handler: (accessor, args) => {
this.setPermissionsForAccount(accessor.get(IQuickInputService), true);
},
}));
this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
group: '2_providers',
command: {
id: `signIn${this.id}`,
title: nls.localize('addAccount', "Sign in to {0}", this.displayName)
},
order: 3
}));
this._proxy.$getSessions(this.id).then(sessions => {
sessions.forEach(session => this.registerSession(session));
});
}
private registerSession(session: modes.AuthenticationSession) {
this._sessionIds.push(session.id);
const menuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, {
group: '1_accounts',
command: {
id: `configureSessions${session.id}`,
title: session.accountName
},
order: 3
});
const manageCommand = CommandsRegistry.registerCommand({
id: `configureSessions${session.id}`,
handler: (accessor, args) => {
const quickInputService = accessor.get(IQuickInputService);
const quickPick = quickInputService.createQuickPick();
const items = [{ label: 'Sign Out' }];
quickPick.items = items;
quickPick.onDidAccept(e => {
const selected = quickPick.selectedItems[0];
if (selected.label === 'Sign Out') {
this.logout(session.id);
}
quickPick.dispose();
});
quickPick.onDidHide(_ => {
quickPick.dispose();
});
quickPick.show();
},
});
this._sessionMenuItems.set(session.id, [menuItem, manageCommand]);
}
async getSessions(): Promise<ReadonlyArray<modes.AuthenticationSession>> {
return (await this._proxy.$getSessions(this.id)).map(session => {
@@ -30,6 +158,24 @@ export class MainThreadAuthenticationProvider {
});
}
async updateSessionItems(): Promise<void> {
const currentSessions = await this._proxy.$getSessions(this.id);
const removedSessionIds = this._sessionIds.filter(id => !currentSessions.some(session => session.id === id));
const addedSessions = currentSessions.filter(session => !this._sessionIds.some(id => id === session.id));
removedSessionIds.forEach(id => {
const disposeables = this._sessionMenuItems.get(id);
if (disposeables) {
disposeables.forEach(disposeable => disposeable.dispose());
this._sessionMenuItems.delete(id);
}
});
addedSessions.forEach(session => this.registerSession(session));
this._sessionIds = currentSessions.map(session => session.id);
}
login(scopes: string[]): Promise<modes.AuthenticationSession> {
return this._proxy.$login(this.id, scopes).then(session => {
return {
@@ -40,8 +186,14 @@ export class MainThreadAuthenticationProvider {
});
}
logout(accountId: string): Promise<void> {
return this._proxy.$logout(this.id, accountId);
logout(sessionId: string): Promise<void> {
return this._proxy.$logout(this.id, sessionId);
}
dispose(): void {
super.dispose();
this._sessionMenuItems.forEach(item => item.forEach(d => d.dispose()));
this._sessionMenuItems.clear();
}
}
@@ -59,8 +211,10 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostAuthentication);
}
$registerAuthenticationProvider(id: string, displayName: string): void {
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName);
async $registerAuthenticationProvider(id: string, displayName: string): Promise<void> {
const dependentBuiltIns = BUILT_IN_AUTH_DEPENDENTS.filter(dependency => dependency.providerId === id);
const provider = new MainThreadAuthenticationProvider(this._proxy, id, displayName, dependentBuiltIns);
this.authenticationService.registerAuthenticationProvider(id, provider);
}
@@ -68,8 +222,8 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
this.authenticationService.unregisterAuthenticationProvider(id);
}
$onDidChangeSessions(id: string): void {
this.authenticationService.sessionsUpdate(id);
$onDidChangeSessions(id: string, event: modes.AuthenticationSessionsChangeEvent): void {
this.authenticationService.sessionsUpdate(id, event);
}
async $getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {

View File

@@ -8,7 +8,7 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx
import { Disposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellsSplice, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellsSplice, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
@@ -127,7 +127,7 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
}
}
async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata | undefined): Promise<void> {
async $updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise<void> {
let controller = this._notebookProviders.get(viewType);
if (controller) {
@@ -135,6 +135,14 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
}
}
async $updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata): Promise<void> {
let controller = this._notebookProviders.get(viewType);
if (controller) {
controller.updateNotebookCellMetadata(resource, handle, metadata);
}
}
async resolveNotebook(viewType: string, uri: URI): Promise<number | undefined> {
let handle = await this._proxy.$resolveNotebook(viewType, uri);
return handle;
@@ -228,25 +236,25 @@ export class MainThreadNotebookController implements IMainNotebookController {
document?.textModel.updateLanguages(languages);
}
updateNotebookMetadata(resource: UriComponents, metadata: NotebookDocumentMetadata | undefined) {
updateNotebookMetadata(resource: UriComponents, metadata: NotebookDocumentMetadata) {
let document = this._mapping.get(URI.from(resource).toString());
document?.textModel.updateNotebookMetadata(metadata);
}
updateNotebookCellMetadata(resource: UriComponents, handle: number, metadata: NotebookCellMetadata) {
let document = this._mapping.get(URI.from(resource).toString());
document?.textModel.updateNotebookCellMetadata(handle, metadata);
}
updateNotebookRenderers(resource: UriComponents, renderers: number[]): void {
let document = this._mapping.get(URI.from(resource).toString());
document?.textModel.updateRenderers(renderers);
}
updateNotebookActiveCell(uri: URI, cellHandle: number): void {
let mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
mainthreadNotebook?.textModel.updateActiveCell(cellHandle);
}
async createRawCell(uri: URI, index: number, language: string, type: CellKind): Promise<NotebookCellTextModel | undefined> {
let cell = await this._proxy.$createEmptyCell(this._viewType, uri, index, language, type);
if (cell) {
let mainCell = new NotebookCellTextModel(URI.revive(cell.uri), cell.handle, cell.source, cell.language, cell.cellKind, cell.outputs);
let mainCell = new NotebookCellTextModel(URI.revive(cell.uri), cell.handle, cell.source, cell.language, cell.cellKind, cell.outputs, cell.metadata);
return mainCell;
}
@@ -263,12 +271,8 @@ export class MainThreadNotebookController implements IMainNotebookController {
return false;
}
async executeNotebookActiveCell(uri: URI): Promise<void> {
let mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
if (mainthreadNotebook && mainthreadNotebook.textModel.activeCell) {
return this._proxy.$executeNotebook(this._viewType, uri, mainthreadNotebook.textModel.activeCell.handle);
}
async executeNotebookCell(uri: URI, handle: number): Promise<void> {
return this._proxy.$executeNotebook(this._viewType, uri, handle);
}
async destoryNotebookDocument(notebook: INotebookTextModel): Promise<void> {

View File

@@ -11,6 +11,7 @@ import { Disposable, DisposableStore, dispose, IDisposable, IReference } from 'v
import { Schemas } from 'vs/base/common/network';
import { basename } from 'vs/base/common/path';
import { isWeb } from 'vs/base/common/platform';
import { isEqual } from 'vs/base/common/resources';
import { escape } from 'vs/base/common/strings';
import { URI, UriComponents } from 'vs/base/common/uri';
import * as modes from 'vs/editor/common/modes';
@@ -28,6 +29,7 @@ import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } fr
import { IEditorInput, IRevertOptions, ISaveOptions } from 'vs/workbench/common/editor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { CustomEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput';
import { CustomDocumentBackupData } from 'vs/workbench/contrib/customEditor/browser/customEditorInputFactory';
import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/customTextEditorModel';
import { WebviewExtensionDescription, WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview';
@@ -70,6 +72,10 @@ class WebviewInputStore {
public get size(): number {
return this._handlesToInputs.size;
}
[Symbol.iterator](): Iterator<WebviewInput> {
return this._handlesToInputs.values();
}
}
class WebviewViewTypeTransformer {
@@ -374,7 +380,10 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
const model = modelType === ModelType.Text
? CustomTextEditorModel.create(this._instantiationService, viewType, resource)
: MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource, cancellation);
: MainThreadCustomEditorModel.create(this._instantiationService, this._proxy, viewType, resource, () => {
return Array.from(this._webviewInputs)
.filter(editor => editor instanceof CustomEditorInput && isEqual(editor.resource, resource)) as CustomEditorInput[];
}, cancellation);
return this._customEditorService.models.add(resource, viewType, model);
}
@@ -548,7 +557,6 @@ namespace HotExitState {
export type State = typeof Allowed | typeof NotAllowed | Pending;
}
const customDocumentFileScheme = 'custom';
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
@@ -562,17 +570,19 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
proxy: extHostProtocol.ExtHostWebviewsShape,
viewType: string,
resource: URI,
getEditors: () => CustomEditorInput[],
cancellation: CancellationToken,
) {
const { editable } = await proxy.$createWebviewCustomEditorDocument(resource, viewType, cancellation);
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable);
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable, getEditors);
}
constructor(
private readonly _proxy: extHostProtocol.ExtHostWebviewsShape,
private readonly _viewType: string,
private readonly _realResource: URI,
private readonly _editorResource: URI,
private readonly _editable: boolean,
private readonly _getEditors: () => CustomEditorInput[],
@IWorkingCopyService workingCopyService: IWorkingCopyService,
@ILabelService private readonly _labelService: ILabelService,
@IFileService private readonly _fileService: IFileService,
@@ -587,9 +597,9 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
dispose() {
if (this._editable) {
this._undoService.removeElements(this._realResource);
this._undoService.removeElements(this._editorResource);
}
this._proxy.$disposeWebviewCustomEditorDocument(this._realResource, this._viewType);
this._proxy.$disposeWebviewCustomEditorDocument(this._editorResource, this._viewType);
super.dispose();
}
@@ -598,15 +608,15 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
public get resource() {
// Make sure each custom editor has a unique resource for backup and edits
return URI.from({
scheme: customDocumentFileScheme,
scheme: Schemas.vscodeCustomEditor,
authority: this._viewType,
path: this._realResource.path,
query: JSON.stringify(this._realResource.toJSON())
path: this._editorResource.path,
query: JSON.stringify(this._editorResource.toJSON()),
});
}
public get name() {
return basename(this._labelService.getUriLabel(this._realResource));
return basename(this._labelService.getUriLabel(this._editorResource));
}
public get capabilities(): WorkingCopyCapabilities {
@@ -645,7 +655,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
this._undoService.pushElement({
type: UndoRedoElementType.Resource,
resource: this._realResource,
resource: this._editorResource,
label: label ?? localize('defaultEditLabel', "Edit"),
undo: () => this.undo(),
redo: () => this.redo(),
@@ -663,11 +673,18 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
}
const undoneEdit = this._edits[this._currentEditIndex];
await this._proxy.$undo(this._realResource, this.viewType, undoneEdit);
this.change(() => {
--this._currentEditIndex;
});
await this._proxy.$undo(this._editorResource, this.viewType, undoneEdit, this.getEditState());
}
private getEditState(): extHostProtocol.CustomDocumentEditState {
return {
allEdits: this._edits,
currentIndex: this._currentEditIndex,
saveIndex: this._savePoint,
};
}
private async redo(): Promise<void> {
@@ -681,10 +698,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
}
const redoneEdit = this._edits[this._currentEditIndex + 1];
await this._proxy.$redo(this._realResource, this.viewType, redoneEdit);
this.change(() => {
++this._currentEditIndex;
});
await this._proxy.$redo(this._editorResource, this.viewType, redoneEdit, this.getEditState());
}
private spliceEdits(editToInsert?: number) {
@@ -696,7 +713,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
: this._edits.splice(start, toRemove);
if (removedEdits.length) {
this._proxy.$disposeEdits(this._realResource, this._viewType, removedEdits);
this._proxy.$disposeEdits(this._editorResource, this._viewType, removedEdits);
}
}
@@ -728,7 +745,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint);
}
this._proxy.$revert(this._realResource, this.viewType, { undoneEdits: editsToUndo, redoneEdits: editsToRedo });
this._proxy.$revert(this._editorResource, this.viewType, { undoneEdits: editsToUndo, redoneEdits: editsToRedo }, this.getEditState());
this.change(() => {
this._currentEditIndex = this._savePoint;
this.spliceEdits();
@@ -739,7 +756,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
if (!this._editable) {
return false;
}
await createCancelablePromise(token => this._proxy.$onSave(this._realResource, this.viewType, token));
await createCancelablePromise(token => this._proxy.$onSave(this._editorResource, this.viewType, token));
this.change(() => {
this._savePoint = this._currentEditIndex;
});
@@ -748,7 +765,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
public async saveAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
if (this._editable) {
await this._proxy.$onSaveAs(this._realResource, this.viewType, targetResource);
await this._proxy.$onSaveAs(this._editorResource, this.viewType, targetResource);
this.change(() => {
this._savePoint = this._currentEditIndex;
});
@@ -761,9 +778,25 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
}
public async backup(): Promise<IWorkingCopyBackup> {
const backupData: IWorkingCopyBackup = {
const editors = this._getEditors();
if (!editors.length) {
throw new Error('No editors found for resource, cannot back up');
}
const primaryEditor = editors[0];
const backupData: IWorkingCopyBackup<CustomDocumentBackupData> = {
meta: {
viewType: this.viewType,
editorResource: this._editorResource,
extension: primaryEditor.extension ? {
id: primaryEditor.extension.id.value,
location: primaryEditor.extension.location,
} : undefined,
webview: {
id: primaryEditor.id,
options: primaryEditor.webview.options,
state: primaryEditor.webview.state,
}
}
};
@@ -777,7 +810,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
const pendingState = new HotExitState.Pending(
createCancelablePromise(token =>
this._proxy.$backup(this._realResource.toJSON(), this.viewType, token)));
this._proxy.$backup(this._editorResource.toJSON(), this.viewType, token)));
this._hotExitState = pendingState;
try {

View File

@@ -202,7 +202,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
login(providerId: string, scopes: string[]): Thenable<vscode.AuthenticationSession> {
return extHostAuthentication.login(extension, providerId, scopes);
},
get onDidChangeSessions(): Event<string[]> {
get onDidChangeSessions(): Event<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }> {
return extHostAuthentication.onDidChangeSessions;
},
};
@@ -549,9 +549,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } }));
},
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable<R>) {
if (typeof options.location === 'object') {
checkProposedApiEnabled(extension);
}
return extHostProgress.withProgress(extension, options, task);
},
createOutputChannel(name: string): vscode.OutputChannel {
@@ -586,9 +583,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
registerWebviewPanelSerializer: (viewType: string, serializer: vscode.WebviewPanelSerializer) => {
return extHostWebviews.registerWebviewPanelSerializer(extension, viewType, serializer);
},
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider, options?: vscode.WebviewPanelOptions) => {
registerCustomEditorProvider: (viewType: string, provider: vscode.CustomEditorProvider | vscode.CustomTextEditorProvider, options?: { webviewOptions?: vscode.WebviewPanelOptions }) => {
checkProposedApiEnabled(extension);
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options);
return extHostWebviews.registerCustomEditorProvider(extension, viewType, provider, options?.webviewOptions);
},
registerDecorationProvider(provider: vscode.DecorationProvider) {
checkProposedApiEnabled(extension);
@@ -1048,12 +1045,12 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
CallHierarchyItem: extHostTypes.CallHierarchyItem,
DebugConsoleMode: extHostTypes.DebugConsoleMode,
Decoration: extHostTypes.Decoration,
WebviewContentState: extHostTypes.WebviewContentState,
UIKind: UIKind,
ColorThemeKind: extHostTypes.ColorThemeKind,
TimelineItem: extHostTypes.TimelineItem,
CellKind: extHostTypes.CellKind,
CellOutputKind: extHostTypes.CellOutputKind
CellOutputKind: extHostTypes.CellOutputKind,
CustomDocument: extHostTypes.CustomDocument,
};
};
}

View File

@@ -160,7 +160,7 @@ export interface MainThreadCommentsShape extends IDisposable {
export interface MainThreadAuthenticationShape extends IDisposable {
$registerAuthenticationProvider(id: string, displayName: string): void;
$unregisterAuthenticationProvider(id: string): void;
$onDidChangeSessions(id: string): void;
$onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
$getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
$loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
}
@@ -626,6 +626,12 @@ export interface WebviewPanelViewStateData {
};
}
export interface CustomDocumentEditState {
readonly allEdits: readonly number[];
readonly currentIndex: number;
readonly saveIndex: number;
}
export interface ExtHostWebviewsShape {
$onMessage(handle: WebviewPanelHandle, message: any): void;
$onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void;
@@ -638,9 +644,9 @@ export interface ExtHostWebviewsShape {
$createWebviewCustomEditorDocument(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<{ editable: boolean }>;
$disposeWebviewCustomEditorDocument(resource: UriComponents, viewType: string): Promise<void>;
$undo(resource: UriComponents, viewType: string, editId: number): Promise<void>;
$redo(resource: UriComponents, viewType: string, editId: number): Promise<void>;
$revert(resource: UriComponents, viewType: string, changes: { undoneEdits: number[], redoneEdits: number[] }): Promise<void>;
$undo(resource: UriComponents, viewType: string, editId: number, state: CustomDocumentEditState): Promise<void>;
$redo(resource: UriComponents, viewType: string, editId: number, state: CustomDocumentEditState): Promise<void>;
$revert(resource: UriComponents, viewType: string, changes: { undoneEdits: number[], redoneEdits: number[] }, state: CustomDocumentEditState): Promise<void>;
$disposeEdits(resourceComponents: UriComponents, viewType: string, editIds: number[]): void;
$onSave(resource: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void>;
@@ -691,7 +697,8 @@ export interface MainThreadNotebookShape extends IDisposable {
$unregisterNotebookRenderer(handle: number): Promise<void>;
$createNotebookDocument(handle: number, viewType: string, resource: UriComponents): Promise<void>;
$updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void>;
$updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata | undefined): Promise<void>;
$updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise<void>;
$updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise<void>;
$spliceNotebookCells(viewType: string, resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): Promise<void>;
$spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void>;
$postMessage(handle: number, value: any): Promise<boolean>;

View File

@@ -17,8 +17,8 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
private _onDidChangeAuthenticationProviders = new Emitter<vscode.AuthenticationProvidersChangeEvent>();
readonly onDidChangeAuthenticationProviders: Event<vscode.AuthenticationProvidersChangeEvent> = this._onDidChangeAuthenticationProviders.event;
private _onDidChangeSessions = new Emitter<string[]>();
readonly onDidChangeSessions: Event<string[]> = this._onDidChangeSessions.event;
private _onDidChangeSessions = new Emitter<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }>();
readonly onDidChangeSessions: Event<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }> = this._onDidChangeSessions.event;
constructor(mainContext: IMainContext) {
this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication);
@@ -85,9 +85,9 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
this._authenticationProviders.set(provider.id, provider);
const listener = provider.onDidChangeSessions(_ => {
this._proxy.$onDidChangeSessions(provider.id);
this._onDidChangeSessions.fire([provider.id]);
const listener = provider.onDidChangeSessions(e => {
this._proxy.$onDidChangeSessions(provider.id, e);
this._onDidChangeSessions.fire({ [provider.id]: e });
});
this._proxy.$registerAuthenticationProvider(provider.id, provider.displayName);

View File

@@ -16,6 +16,12 @@ import { INotebookDisplayOrder, ITransformedDisplayOutputDto, IOrderedMimeType,
import { ISplice } from 'vs/base/common/sequence';
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
const notebookDocumentMetadataDefaults: vscode.NotebookDocumentMetadata = {
editable: true,
cellEditable: true,
cellRunnable: true
};
export class ExtHostCell implements vscode.NotebookCell {
public source: string[];
@@ -27,13 +33,16 @@ export class ExtHostCell implements vscode.NotebookCell {
private _outputMapping = new Set<vscode.CellOutput>();
constructor(
private viewType: string,
private documentUri: URI,
readonly handle: number,
readonly uri: URI,
private _content: string,
public cellKind: CellKind,
public language: string,
outputs: any[],
public metadata: vscode.NotebookCellMetadata | undefined,
private _metadata: vscode.NotebookCellMetadata | undefined,
private _proxy: MainThreadNotebookShape
) {
this.source = this._content.split(/\r|\n|\r\n/g);
this._outputs = outputs;
@@ -62,6 +71,20 @@ export class ExtHostCell implements vscode.NotebookCell {
this._onDidChangeOutputs.fire(diffs);
}
get metadata() {
return this._metadata;
}
set metadata(newMetadata: vscode.NotebookCellMetadata | undefined) {
const newMetadataWithDefaults: vscode.NotebookCellMetadata | undefined = newMetadata ? {
editable: newMetadata.editable,
runnable: newMetadata.runnable
} : undefined;
this._metadata = newMetadataWithDefaults;
this._proxy.$updateNotebookCellMetadata(this.viewType, this.documentUri, this.handle, newMetadataWithDefaults);
}
getContent(): string {
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
return this._textDocument.getText();
@@ -131,14 +154,14 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
this._proxy.$updateNotebookLanguages(this.viewType, this.uri, this._languages);
}
private _metadata: vscode.NotebookDocumentMetadata | undefined = undefined;
private _metadata: vscode.NotebookDocumentMetadata | undefined = notebookDocumentMetadataDefaults;
get metadata() {
return this._metadata;
}
set metadata(newMetadata: vscode.NotebookDocumentMetadata | undefined) {
this._metadata = newMetadata;
this._metadata = newMetadata || notebookDocumentMetadataDefaults;
this._proxy.$updateNotebookMetadata(this.viewType, this.uri, this._metadata);
}
@@ -201,6 +224,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
language: cell.language,
cellKind: cell.cellKind,
outputs: outputs,
metadata: cell.metadata,
isDirty: false
};
});
@@ -346,7 +370,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
constructor(
viewType: string,
private readonly viewType: string,
readonly id: string,
public uri: URI,
private _proxy: MainThreadNotebookShape,
@@ -381,7 +405,7 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
createCell(content: string, language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): vscode.NotebookCell {
const handle = ExtHostNotebookEditor._cellhandlePool++;
const uri = CellUri.generate(this.document.uri, handle);
const cell = new ExtHostCell(handle, uri, content, type, language, outputs, metadata);
const cell = new ExtHostCell(this.viewType, this.uri, handle, uri, content, type, language, outputs, metadata, this._proxy);
return cell;
}
@@ -469,9 +493,8 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
}
}
}
return arg;
}
return arg;
}
});
}
@@ -580,7 +603,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
let editor = this._editors.get(URI.revive(uri).toString());
let document = this._documents.get(URI.revive(uri).toString());
let rawCell = editor?.editor.createCell('', language, type, [], undefined) as ExtHostCell;
let rawCell = editor?.editor.createCell('', language, type, [], { editable: true, runnable: true }) as ExtHostCell;
document?.insertCell(index, rawCell!);
let allDocuments = this._documentsAndEditors.allDocuments();

View File

@@ -4,16 +4,19 @@
*--------------------------------------------------------------------------------------------*/
import { coalesce, equals } from 'vs/base/common/arrays';
import { escapeCodicons } from 'vs/base/common/codicons';
import { illegalArgument } from 'vs/base/common/errors';
import { Emitter } from 'vs/base/common/event';
import { IRelativePattern } from 'vs/base/common/glob';
import { isMarkdownString } from 'vs/base/common/htmlContent';
import { startsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid';
import type * as vscode from 'vscode';
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { escapeCodicons } from 'vs/base/common/codicons';
import type * as vscode from 'vscode';
import { Cache } from './cache';
import { assertIsDefined } from 'vs/base/common/types';
function es5ClassCompat(target: Function): any {
///@ts-ignore
@@ -2538,13 +2541,6 @@ export class Decoration {
bubble?: boolean;
}
export enum WebviewContentState {
Readonly = 1,
Unchanged = 2,
Dirty = 3,
}
//#region Theming
@es5ClassCompat
@@ -2584,3 +2580,84 @@ export class TimelineItem implements vscode.TimelineItem {
}
//#endregion Timeline
//#region Custom Editors
interface EditState {
readonly allEdits: readonly number[];
readonly currentIndex: number;
readonly saveIndex: number;
}
export class CustomDocument<EditType = unknown> implements vscode.CustomDocument<EditType> {
readonly #edits = new Cache<EditType>('edits');
#editState: EditState;
readonly #viewType: string;
readonly #uri: vscode.Uri;
constructor(viewType: string, uri: vscode.Uri) {
this.#viewType = viewType;
this.#uri = uri;
this.#editState = {
allEdits: [],
currentIndex: 0,
saveIndex: 0
};
}
//#region Public API
public get viewType(): string { return this.#viewType; }
public get uri(): vscode.Uri { return this.#uri; }
#onDidDispose = new Emitter<void>();
public readonly onDidDispose = this.#onDidDispose.event;
get appliedEdits() {
return this.#editState.allEdits.slice(0, this.#editState.currentIndex + 1)
.map(id => this._getEdit(id));
}
get savedEdits() {
return this.#editState.allEdits.slice(0, this.#editState.saveIndex + 1)
.map(id => this._getEdit(id));
}
//#endregion
/** @internal */ _dispose(): void {
this.#onDidDispose.fire();
this.#onDidDispose.dispose();
}
/** @internal */ _updateEditState(state: EditState) {
this.#editState = state;
}
/** @internal*/ _getEdit(editId: number): EditType {
return assertIsDefined(this.#edits.get(editId, 0));
}
/** @internal*/ _disposeEdits(editIds: number[]) {
for (const editId of editIds) {
this.#edits.delete(editId);
}
}
/** @internal*/ _addEdit(edit: EditType): number {
const id = this.#edits.add([edit]);
this.#editState = {
allEdits: [...this.#editState.allEdits.slice(0, this.#editState.currentIndex), id],
currentIndex: this.#editState.currentIndex + 1,
saveIndex: this.#editState.saveIndex,
};
return id;
}
}
// #endregion

View File

@@ -18,9 +18,8 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
import { asWebviewUri, WebviewInitData } from 'vs/workbench/api/common/shared/webview';
import type * as vscode from 'vscode';
import { Cache } from './cache';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewExtensionDescription, WebviewPanelHandle, WebviewPanelViewStateData } from './extHost.protocol';
import { Disposable as VSCodeDisposable } from './extHostTypes';
import * as extHostProtocol from './extHost.protocol';
import * as extHostTypes from './extHostTypes';
type IconPath = URI | { light: URI, dark: URI };
@@ -33,8 +32,8 @@ export class ExtHostWebview implements vscode.Webview {
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
constructor(
private readonly _handle: WebviewPanelHandle,
private readonly _proxy: MainThreadWebviewsShape,
private readonly _handle: extHostProtocol.WebviewPanelHandle,
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape,
private _options: vscode.WebviewOptions,
private readonly _initData: WebviewInitData,
private readonly _workspace: IExtHostWorkspace | undefined,
@@ -99,8 +98,8 @@ export class ExtHostWebview implements vscode.Webview {
export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPanel {
private readonly _handle: WebviewPanelHandle;
private readonly _proxy: MainThreadWebviewsShape;
private readonly _handle: extHostProtocol.WebviewPanelHandle;
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape;
private readonly _viewType: string;
private _title: string;
private _iconPath?: IconPath;
@@ -121,8 +120,8 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
public readonly onDidChangeViewState = this.#onDidChangeViewState.event;
constructor(
handle: WebviewPanelHandle,
proxy: MainThreadWebviewsShape,
handle: extHostProtocol.WebviewPanelHandle,
proxy: extHostProtocol.MainThreadWebviewsShape,
viewType: string,
title: string,
viewColumn: vscode.ViewColumn | undefined,
@@ -246,114 +245,14 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
}
}
class CustomDocument extends Disposable implements vscode.CustomDocument {
public static create(
viewType: string,
uri: vscode.Uri,
editingDelegate: vscode.CustomEditorEditingDelegate | undefined
) {
return Object.seal(new CustomDocument(viewType, uri, editingDelegate));
}
// Explicitly initialize all properties as we seal the object after creation!
readonly #_edits = new Cache<unknown>('edits');
readonly #viewType: string;
readonly #uri: vscode.Uri;
readonly #editingDelegate: vscode.CustomEditorEditingDelegate | undefined;
private constructor(
viewType: string,
uri: vscode.Uri,
editingDelegate: vscode.CustomEditorEditingDelegate | undefined,
) {
super();
this.#viewType = viewType;
this.#uri = uri;
this.#editingDelegate = editingDelegate;
}
dispose() {
this.#onDidDispose.fire();
super.dispose();
}
//#region Public API
public get viewType(): string { return this.#viewType; }
public get uri(): vscode.Uri { return this.#uri; }
#onDidDispose = this._register(new Emitter<void>());
public readonly onDidDispose = this.#onDidDispose.event;
public userData: unknown = undefined;
//#endregion
//#region Internal
/** @internal*/ async _revert(changes: { undoneEdits: number[], redoneEdits: number[] }) {
const editing = this.getEditingDelegate();
const undoneEdits = changes.undoneEdits.map(id => this.#_edits.get(id, 0));
const appliedEdits = changes.redoneEdits.map(id => this.#_edits.get(id, 0));
return editing.revert(this, { undoneEdits, appliedEdits });
}
/** @internal*/ _undo(editId: number) {
const editing = this.getEditingDelegate();
const edit = this.#_edits.get(editId, 0);
return editing.undoEdits(this, [edit]);
}
/** @internal*/ _redo(editId: number) {
const editing = this.getEditingDelegate();
const edit = this.#_edits.get(editId, 0);
return editing.applyEdits(this, [edit]);
}
/** @internal*/ _save(cancellation: CancellationToken) {
return this.getEditingDelegate().save(this, cancellation);
}
/** @internal*/ _saveAs(target: vscode.Uri) {
return this.getEditingDelegate().saveAs(this, target);
}
/** @internal*/ _backup(cancellation: CancellationToken) {
return this.getEditingDelegate().backup(this, cancellation);
}
/** @internal*/ _disposeEdits(editIds: number[]) {
for (const editId of editIds) {
this.#_edits.delete(editId);
}
}
/** @internal*/ _pushEdit(edit: unknown): number {
return this.#_edits.add([edit]);
}
//#endregion
private getEditingDelegate(): vscode.CustomEditorEditingDelegate {
if (!this.#editingDelegate) {
throw new Error('Document is not editable');
}
return this.#editingDelegate;
}
}
class WebviewDocumentStore {
private readonly _documents = new Map<string, CustomDocument>();
private readonly _documents = new Map<string, extHostTypes.CustomDocument>();
public get(viewType: string, resource: vscode.Uri): CustomDocument | undefined {
public get(viewType: string, resource: vscode.Uri): extHostTypes.CustomDocument | undefined {
return this._documents.get(this.key(viewType, resource));
}
public add(document: CustomDocument) {
public add(document: extHostTypes.CustomDocument) {
const key = this.key(document.viewType, document.uri);
if (this._documents.has(key)) {
throw new Error(`Document already exists for viewType:${document.viewType} resource:${document.uri}`);
@@ -361,7 +260,7 @@ class WebviewDocumentStore {
this._documents.set(key, document);
}
public delete(document: CustomDocument) {
public delete(document: extHostTypes.CustomDocument) {
const key = this.key(document.viewType, document.uri);
this._documents.delete(key);
}
@@ -406,18 +305,18 @@ class EditorProviderStore {
throw new Error(`Provider for viewType:${viewType} already registered`);
}
this._providers.set(viewType, { type, extension, provider } as ProviderEntry);
return new VSCodeDisposable(() => this._providers.delete(viewType));
return new extHostTypes.Disposable(() => this._providers.delete(viewType));
}
}
export class ExtHostWebviews implements ExtHostWebviewsShape {
export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
private static newHandle(): WebviewPanelHandle {
private static newHandle(): extHostProtocol.WebviewPanelHandle {
return generateUuid();
}
private readonly _proxy: MainThreadWebviewsShape;
private readonly _webviewPanels = new Map<WebviewPanelHandle, ExtHostWebviewEditor>();
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape;
private readonly _webviewPanels = new Map<extHostProtocol.WebviewPanelHandle, ExtHostWebviewEditor>();
private readonly _serializers = new Map<string, {
readonly serializer: vscode.WebviewPanelSerializer;
@@ -429,14 +328,14 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
private readonly _documents = new WebviewDocumentStore();
constructor(
mainContext: IMainContext,
mainContext: extHostProtocol.IMainContext,
private readonly initData: WebviewInitData,
private readonly workspace: IExtHostWorkspace | undefined,
private readonly _logService: ILogService,
private readonly _deprecationService: IExtHostApiDeprecationService,
private readonly _extHostDocuments: ExtHostDocuments,
) {
this._proxy = mainContext.getProxy(MainContext.MainThreadWebviews);
this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews);
}
public createWebviewPanel(
@@ -473,7 +372,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
this._serializers.set(viewType, { serializer, extension });
this._proxy.$registerSerializer(viewType);
return new VSCodeDisposable(() => {
return new extHostTypes.Disposable(() => {
this._serializers.delete(viewType);
this._proxy.$unregisterSerializer(viewType);
});
@@ -497,21 +396,21 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
if (provider.editingDelegate) {
disposables.add(provider.editingDelegate.onDidEdit(e => {
const document = e.document;
const editId = (document as CustomDocument)._pushEdit(e.edit);
const editId = (document as extHostTypes.CustomDocument)._addEdit(e.edit);
this._proxy.$onDidEdit(document.uri, document.viewType, editId, e.label);
}));
}
}
return VSCodeDisposable.from(
return extHostTypes.Disposable.from(
disposables,
new VSCodeDisposable(() => {
new extHostTypes.Disposable(() => {
this._proxy.$unregisterEditorProvider(viewType);
}));
}
public $onMessage(
handle: WebviewPanelHandle,
handle: extHostProtocol.WebviewPanelHandle,
message: any
): void {
const panel = this.getWebviewPanel(handle);
@@ -521,13 +420,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
public $onMissingCsp(
_handle: WebviewPanelHandle,
_handle: extHostProtocol.WebviewPanelHandle,
extensionId: string
): void {
this._logService.warn(`${extensionId} created a webview without a content security policy: https://aka.ms/vscode-webview-missing-csp`);
}
public $onDidChangeWebviewPanelViewStates(newStates: WebviewPanelViewStateData): void {
public $onDidChangeWebviewPanelViewStates(newStates: extHostProtocol.WebviewPanelViewStateData): void {
const handles = Object.keys(newStates);
// Notify webviews of state changes in the following order:
// - Non-visible
@@ -560,7 +459,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
}
async $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void> {
async $onDidDisposeWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): Promise<void> {
const panel = this.getWebviewPanel(handle);
if (panel) {
panel.dispose();
@@ -569,7 +468,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
async $deserializeWebviewPanel(
webviewHandle: WebviewPanelHandle,
webviewHandle: extHostProtocol.WebviewPanelHandle,
viewType: string,
title: string,
state: any,
@@ -599,9 +498,8 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
}
const revivedResource = URI.revive(resource);
const document = CustomDocument.create(viewType, revivedResource, entry.provider.editingDelegate);
await entry.provider.resolveCustomDocument(document, cancellation);
this._documents.add(document);
const document = await entry.provider.openCustomDocument(revivedResource, cancellation);
this._documents.add(document as extHostTypes.CustomDocument);
return {
editable: !!entry.provider.editingDelegate,
};
@@ -620,12 +518,12 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
const revivedResource = URI.revive(resource);
const document = this.getCustomDocument(viewType, revivedResource);
this._documents.delete(document);
document.dispose();
document._dispose();
}
async $resolveWebviewEditor(
resource: UriComponents,
handle: WebviewPanelHandle,
handle: extHostProtocol.WebviewPanelHandle,
viewType: string,
title: string,
position: EditorViewColumn,
@@ -686,50 +584,73 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
await (entry.provider as vscode.CustomTextEditorProvider).moveCustomTextEditor!(document, webview, CancellationToken.None);
}
async $undo(resourceComponents: UriComponents, viewType: string, editId: number): Promise<void> {
async $undo(resourceComponents: UriComponents, viewType: string, editId: number, state: extHostProtocol.CustomDocumentEditState): Promise<void> {
const delegate = this.getEditingDelegate(viewType);
const document = this.getCustomDocument(viewType, resourceComponents);
return document._undo(editId);
document._updateEditState(state);
return delegate.undoEdits(document, [document._getEdit(editId)]);
}
async $redo(resourceComponents: UriComponents, viewType: string, editId: number): Promise<void> {
async $redo(resourceComponents: UriComponents, viewType: string, editId: number, state: extHostProtocol.CustomDocumentEditState): Promise<void> {
const delegate = this.getEditingDelegate(viewType);
const document = this.getCustomDocument(viewType, resourceComponents);
return document._redo(editId);
document._updateEditState(state);
return delegate.applyEdits(document, [document._getEdit(editId)]);
}
async $revert(resourceComponents: UriComponents, viewType: string, changes: { undoneEdits: number[], redoneEdits: number[] }): Promise<void> {
async $revert(resourceComponents: UriComponents, viewType: string, changes: { undoneEdits: number[], redoneEdits: number[] }, state: extHostProtocol.CustomDocumentEditState): Promise<void> {
const delegate = this.getEditingDelegate(viewType);
const document = this.getCustomDocument(viewType, resourceComponents);
return document._revert(changes);
const undoneEdits = changes.undoneEdits.map(id => document._getEdit(id));
const appliedEdits = changes.redoneEdits.map(id => document._getEdit(id));
document._updateEditState(state);
return delegate.revert(document, { undoneEdits, appliedEdits });
}
async $onSave(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
const delegate = this.getEditingDelegate(viewType);
const document = this.getCustomDocument(viewType, resourceComponents);
return document._save(cancellation);
return delegate.save(document, cancellation);
}
async $onSaveAs(resourceComponents: UriComponents, viewType: string, targetResource: UriComponents): Promise<void> {
const delegate = this.getEditingDelegate(viewType);
const document = this.getCustomDocument(viewType, resourceComponents);
return document._saveAs(URI.revive(targetResource));
return delegate.saveAs(document, URI.revive(targetResource));
}
async $backup(resourceComponents: UriComponents, viewType: string, cancellation: CancellationToken): Promise<void> {
const delegate = this.getEditingDelegate(viewType);
const document = this.getCustomDocument(viewType, resourceComponents);
return document._backup(cancellation);
return delegate.backup(document, cancellation);
}
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
private getWebviewPanel(handle: extHostProtocol.WebviewPanelHandle): ExtHostWebviewEditor | undefined {
return this._webviewPanels.get(handle);
}
private getCustomDocument(viewType: string, resource: UriComponents): CustomDocument {
private getCustomDocument(viewType: string, resource: UriComponents): extHostTypes.CustomDocument {
const document = this._documents.get(viewType, URI.revive(resource));
if (!document) {
throw new Error('No webview editor custom document found');
}
return document;
}
private getEditingDelegate(viewType: string): vscode.CustomEditorEditingDelegate {
const entry = this._editorProviders.get(viewType);
if (!entry) {
throw new Error(`No provider found for '${viewType}'`);
}
const delegate = (entry.provider as vscode.CustomEditorProvider).editingDelegate;
if (!delegate) {
throw new Error(`Provider for ${viewType}' does not support editing`);
}
return delegate;
}
}
function toExtensionData(extension: IExtensionDescription): WebviewExtensionDescription {
function toExtensionData(extension: IExtensionDescription): extHostProtocol.WebviewExtensionDescription {
return { id: extension.identifier, location: extension.extensionLocation };
}