mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-06 23:06:20 +01:00
Hookup experimental undo for customEditors
This currently is not connected to any actual actions in the editor
This commit is contained in:
@@ -23,8 +23,7 @@ export function activate(context: vscode.ExtensionContext) {
|
||||
PreviewManager.viewType,
|
||||
{
|
||||
async resolveWebviewEditor({ resource }, editor: vscode.WebviewPanel): Promise<vscode.WebviewEditorCapabilities> {
|
||||
previewManager.resolve(resource, editor);
|
||||
return {};
|
||||
return previewManager.resolve(resource, editor);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export class PreviewManager {
|
||||
public resolve(
|
||||
resource: vscode.Uri,
|
||||
webviewEditor: vscode.WebviewPanel,
|
||||
) {
|
||||
): vscode.WebviewEditorCapabilities {
|
||||
const preview = new Preview(this.extensionRoot, resource, webviewEditor, this.sizeStatusBarEntry, this.zoomStatusBarEntry);
|
||||
this._previews.add(preview);
|
||||
this.setActivePreview(preview);
|
||||
@@ -42,6 +42,17 @@ export class PreviewManager {
|
||||
this.setActivePreview(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
const onEdit = new vscode.EventEmitter<{ now: number }>();
|
||||
return {
|
||||
editingCapability: {
|
||||
onEdit: onEdit.event,
|
||||
save: async () => { },
|
||||
hotExit: async () => { },
|
||||
applyEdits: async () => { },
|
||||
undoEdits: async (edits) => { console.log('undo', edits); },
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public get activePreview() { return this._activePreview; }
|
||||
|
||||
@@ -27,6 +27,7 @@ import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
import { CustomEditorModel } from 'vs/workbench/contrib/customEditor/browser/customEditorModel';
|
||||
|
||||
/**
|
||||
* Bi-directional map between webview handles and inputs.
|
||||
@@ -94,6 +95,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
private readonly _webviewInputs = new WebviewInputStore();
|
||||
private readonly _revivers = new Map<string, IDisposable>();
|
||||
private readonly _editorProviders = new Map<string, IDisposable>();
|
||||
private readonly _models = new Map<string, CustomEditorModel>();
|
||||
|
||||
constructor(
|
||||
context: extHostProtocol.IExtHostContext,
|
||||
@@ -261,7 +263,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
canResolve: (webviewInput) => {
|
||||
return webviewInput instanceof CustomFileEditorInput && webviewInput.viewType === viewType;
|
||||
},
|
||||
resolveWebview: async (webviewInput) => {
|
||||
resolveWebview: async (webviewInput: CustomFileEditorInput) => {
|
||||
const handle = webviewInput.id;
|
||||
this._webviewInputs.add(handle, webviewInput);
|
||||
this.hookupWebviewEventDelegate(handle, webviewInput);
|
||||
@@ -269,6 +271,18 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
webviewInput.webview.options = options;
|
||||
webviewInput.webview.extension = extension;
|
||||
|
||||
const model = new CustomEditorModel();
|
||||
webviewInput.setModel(model);
|
||||
this._models.set(handle, model);
|
||||
|
||||
webviewInput.onDispose(() => {
|
||||
this._models.delete(handle);
|
||||
});
|
||||
|
||||
model.onUndo(edit => {
|
||||
this._proxy.$undoEdits(handle, [edit]);
|
||||
});
|
||||
|
||||
try {
|
||||
await this._proxy.$resolveWebviewEditor(
|
||||
webviewInput.getResource(),
|
||||
@@ -296,11 +310,18 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
this._editorProviders.delete(viewType);
|
||||
}
|
||||
|
||||
public $onEdit(handle: extHostProtocol.WebviewPanelHandle, editJson: string): void {
|
||||
public $onEdit(handle: extHostProtocol.WebviewPanelHandle, editData: string): void {
|
||||
const webview = this.getWebviewInput(handle);
|
||||
if (!(webview instanceof CustomFileEditorInput)) {
|
||||
throw new Error(`Webview is not a webview editor`);
|
||||
throw new Error('Webview is not a webview editor');
|
||||
}
|
||||
|
||||
const model = this._models.get(handle);
|
||||
if (!model) {
|
||||
throw new Error('Could not find model for webview editor');
|
||||
}
|
||||
|
||||
model.makeEdit(editData);
|
||||
}
|
||||
|
||||
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) {
|
||||
|
||||
@@ -589,6 +589,7 @@ export interface ExtHostWebviewsShape {
|
||||
$onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise<void>;
|
||||
$deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise<void>;
|
||||
$undoEdits(handle: WebviewPanelHandle, edits: string[]): void;
|
||||
}
|
||||
|
||||
export interface MainThreadUrlsShape extends IDisposable {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
@@ -93,6 +93,7 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
}
|
||||
|
||||
export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPanel {
|
||||
|
||||
private readonly _handle: WebviewPanelHandle;
|
||||
private readonly _proxy: MainThreadWebviewsShape;
|
||||
private readonly _viewType: string;
|
||||
@@ -112,6 +113,7 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
readonly _onDidChangeViewStateEmitter = this._register(new Emitter<vscode.WebviewPanelOnDidChangeViewStateEvent>());
|
||||
public readonly onDidChangeViewState: Event<vscode.WebviewPanelOnDidChangeViewStateEvent> = this._onDidChangeViewStateEmitter.event;
|
||||
_capabilities: vscode.WebviewEditorCapabilities;
|
||||
|
||||
constructor(
|
||||
handle: WebviewPanelHandle,
|
||||
@@ -233,8 +235,17 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
});
|
||||
}
|
||||
|
||||
_addDisposable(disposable: IDisposable) {
|
||||
this._register(disposable);
|
||||
_setCapabilities(capabilities: vscode.WebviewEditorCapabilities) {
|
||||
this._capabilities = capabilities;
|
||||
if (capabilities.editingCapability) {
|
||||
this._register(capabilities.editingCapability.onEdit(edit => {
|
||||
this._proxy.$onEdit(this._handle, JSON.stringify(edit));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
_undoEdits(edits: string[]): void {
|
||||
this._capabilities.editingCapability?.undoEdits(edits);
|
||||
}
|
||||
|
||||
private assertNotDisposed() {
|
||||
@@ -402,10 +413,6 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
await serializer.deserializeWebviewPanel(revivedPanel, state);
|
||||
}
|
||||
|
||||
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
|
||||
async $resolveWebviewEditor(
|
||||
resource: UriComponents,
|
||||
handle: WebviewPanelHandle,
|
||||
@@ -424,19 +431,19 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
|
||||
const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview);
|
||||
this._webviewPanels.set(handle, revivedPanel);
|
||||
const capabilities = await provider.resolveWebviewEditor({ resource: URI.revive(resource) }, revivedPanel);
|
||||
revivedPanel._addDisposable(this.hookupCapabilities(handle, capabilities));
|
||||
revivedPanel._setCapabilities(capabilities);
|
||||
}
|
||||
|
||||
private hookupCapabilities(handle: WebviewPanelHandle, capabilities: vscode.WebviewEditorCapabilities): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
if (capabilities.editingCapability) {
|
||||
disposables.add(capabilities.editingCapability.onEdit(edit => {
|
||||
this._proxy.$onEdit(handle, JSON.stringify(edit));
|
||||
}));
|
||||
$undoEdits(handle: WebviewPanelHandle, edits: string[]): void {
|
||||
const panel = this.getWebviewPanel(handle);
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
panel._undoEdits(edits);
|
||||
}
|
||||
|
||||
return disposables;
|
||||
private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
|
||||
return this._webviewPanels.get(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,12 +15,14 @@ import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IEditorInput, Verbosity } from 'vs/workbench/common/editor';
|
||||
import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
|
||||
import { CustomEditorModel } from './customEditorModel';
|
||||
|
||||
export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput {
|
||||
|
||||
public static typeId = 'workbench.editors.webviewEditor';
|
||||
|
||||
private readonly _editorResource: URI;
|
||||
private _model?: CustomEditorModel;
|
||||
|
||||
constructor(
|
||||
resource: URI,
|
||||
@@ -105,4 +107,16 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput {
|
||||
return this.longTitle;
|
||||
}
|
||||
}
|
||||
|
||||
public setModel(model: CustomEditorModel) {
|
||||
if (this._model) {
|
||||
throw new Error('Model is already set');
|
||||
}
|
||||
this._model = model;
|
||||
this._register(model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return this._model ? this._model.isDirty() : false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
type Edit = string;
|
||||
|
||||
export class CustomEditorModel extends Disposable {
|
||||
|
||||
private _currentEditIndex: number = 0;
|
||||
private _savePoint: number = -1;
|
||||
private _edits: Array<Edit> = [];
|
||||
|
||||
protected readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChangeDirty: Event<void> = this._onDidChangeDirty.event;
|
||||
|
||||
protected readonly _onUndo: Emitter<Edit> = this._register(new Emitter<Edit>());
|
||||
readonly onUndo: Event<Edit> = this._onUndo.event;
|
||||
|
||||
public makeEdit(data: string): void {
|
||||
this._edits.splice(this._currentEditIndex, this._edits.length - this._currentEditIndex, data);
|
||||
this._currentEditIndex = this._edits.length - 1;
|
||||
this.updateDirty();
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return this._edits.length > 0 && this._savePoint !== this._edits.length;
|
||||
}
|
||||
|
||||
private updateDirty() {
|
||||
this._onDidChangeDirty.fire();
|
||||
}
|
||||
|
||||
public save() {
|
||||
this._savePoint = this._edits.length;
|
||||
this.updateDirty();
|
||||
}
|
||||
|
||||
public undo() {
|
||||
if (this._currentEditIndex >= 0) {
|
||||
const undoneEdit = this._edits[this._currentEditIndex];
|
||||
--this._currentEditIndex;
|
||||
this._onUndo.fire(undoneEdit);
|
||||
}
|
||||
this.updateDirty();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user