mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 13:00:58 +00:00
Add CustomDocumentContentChangeEvent
For #77131 - Rename `onDidEdit` to `onDidChange` - Allow custom editors to fire a `CustomDocumentContentChangeEvent` that only marks the editor as dirty but does not enable undo/redo. The only way for editor to get out of this dirty state is to either save or revert the file
This commit is contained in:
parent
90bc36a982
commit
5426f5ff70
|
@ -27,13 +27,7 @@ module.exports = new (_a = class ApiEventNaming {
|
|||
['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node) => {
|
||||
var _a, _b;
|
||||
const def = (_b = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.parent;
|
||||
let ident;
|
||||
if ((def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.Identifier) {
|
||||
ident = def;
|
||||
}
|
||||
else if (((def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || (def === null || def === void 0 ? void 0 : def.type) === experimental_utils_1.AST_NODE_TYPES.ClassProperty) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
|
||||
ident = def.key;
|
||||
}
|
||||
const ident = this.getIdent(def);
|
||||
if (!ident) {
|
||||
// event on unknown structure...
|
||||
return context.report({
|
||||
|
@ -76,6 +70,18 @@ module.exports = new (_a = class ApiEventNaming {
|
|||
}
|
||||
};
|
||||
}
|
||||
getIdent(def) {
|
||||
if (!def) {
|
||||
return;
|
||||
}
|
||||
if (def.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
|
||||
return def;
|
||||
}
|
||||
else if ((def.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || def.type === experimental_utils_1.AST_NODE_TYPES.ClassProperty) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) {
|
||||
return def.key;
|
||||
}
|
||||
return this.getIdent(def.parent);
|
||||
}
|
||||
},
|
||||
_a._nameRegExp = /on(Did|Will)([A-Z][a-z]+)([A-Z][a-z]+)?/,
|
||||
_a);
|
||||
|
|
|
@ -32,14 +32,7 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule {
|
|||
['TSTypeAnnotation TSTypeReference Identifier[name="Event"]']: (node: any) => {
|
||||
|
||||
const def = (<TSESTree.Identifier>node).parent?.parent?.parent;
|
||||
let ident: TSESTree.Identifier | undefined;
|
||||
|
||||
if (def?.type === AST_NODE_TYPES.Identifier) {
|
||||
ident = def;
|
||||
|
||||
} else if ((def?.type === AST_NODE_TYPES.TSPropertySignature || def?.type === AST_NODE_TYPES.ClassProperty) && def.key.type === AST_NODE_TYPES.Identifier) {
|
||||
ident = def.key;
|
||||
}
|
||||
const ident = this.getIdent(def);
|
||||
|
||||
if (!ident) {
|
||||
// event on unknown structure...
|
||||
|
@ -87,5 +80,19 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
private getIdent(def: TSESTree.Node | undefined): TSESTree.Identifier | undefined {
|
||||
if (!def) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (def.type === AST_NODE_TYPES.Identifier) {
|
||||
return def;
|
||||
} else if ((def.type === AST_NODE_TYPES.TSPropertySignature || def.type === AST_NODE_TYPES.ClassProperty) && def.key.type === AST_NODE_TYPES.Identifier) {
|
||||
return def.key;
|
||||
}
|
||||
|
||||
return this.getIdent(def.parent);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
28
src/vs/vscode.proposed.d.ts
vendored
28
src/vs/vscode.proposed.d.ts
vendored
|
@ -1176,6 +1176,8 @@ declare module 'vscode' {
|
|||
|
||||
/**
|
||||
* Event triggered by extensions to signal to VS Code that an edit has occurred on an [`EditableCustomDocument`](#EditableCustomDocument).
|
||||
*
|
||||
* @see [`EditableCustomDocument.onDidChange`](#EditableCustomDocument.onDidChange).
|
||||
*/
|
||||
interface CustomDocumentEditEvent {
|
||||
/**
|
||||
|
@ -1200,6 +1202,16 @@ declare module 'vscode' {
|
|||
readonly label?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event triggered by extensions to signal to VS Code that the content of a [`EditableCustomDocument`](#EditableCustomDocument)
|
||||
* has changed.
|
||||
*
|
||||
* @see [`EditableCustomDocument.onDidChange`](#EditableCustomDocument.onDidChange).
|
||||
*/
|
||||
interface CustomDocumentContentChangeEvent {
|
||||
// marker interface
|
||||
}
|
||||
|
||||
/**
|
||||
* A backup for an [`EditableCustomDocument`](#EditableCustomDocument).
|
||||
*/
|
||||
|
@ -1265,10 +1277,20 @@ declare module 'vscode' {
|
|||
* anything from changing some text, to cropping an image, to reordering a list. Your extension is free to
|
||||
* define what an edit is and what data is stored on each edit.
|
||||
*
|
||||
* Firing this will cause VS Code to mark the editors as being dirty. This also allows the user to then undo and
|
||||
* redo the edit in the custom editor.
|
||||
* Firing `onDidChange` causes VS Code to mark the editors as being dirty. This is cleared when the user either
|
||||
* saves or reverts the file.
|
||||
*
|
||||
* Editors that support undo/redo must fire a `CustomDocumentEditEvent` whenever an edit happens. This allows
|
||||
* users to undo and redo the edit using VS Code's standard VS Code keyboard shortcuts. VS Code will also mark
|
||||
* the editor as no longer being dirty if the user undoes all edits to the last saved state.
|
||||
*
|
||||
* Editors that support editing but cannot use VS Code's standard undo/redo mechanism must fire a `CustomDocumentContentChangeEvent`.
|
||||
* The only way for a user to clear the dirty state of an editor that does not support undo/redo is to either
|
||||
* `save` or `revert` the file.
|
||||
*
|
||||
* An editor should only ever fire `CustomDocumentEditEvent` events, or only ever fire `CustomDocumentContentChangeEvent` events.
|
||||
*/
|
||||
readonly onDidEdit: Event<CustomDocumentEditEvent>;
|
||||
readonly onDidChange: Event<CustomDocumentEditEvent> | Event<CustomDocumentContentChangeEvent>;
|
||||
|
||||
/**
|
||||
* Revert a custom editor to its last saved state.
|
||||
|
|
|
@ -425,15 +425,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
}
|
||||
|
||||
public async $onDidEdit(resourceComponents: UriComponents, viewType: string, editId: number, label: string | undefined): Promise<void> {
|
||||
const resource = URI.revive(resourceComponents);
|
||||
const model = await this._customEditorService.models.get(resource, viewType);
|
||||
if (!model || !(model instanceof MainThreadCustomEditorModel)) {
|
||||
throw new Error('Could not find model for webview editor');
|
||||
}
|
||||
|
||||
const model = await this.getCustomEditorModel(resourceComponents, viewType);
|
||||
model.pushEdit(editId, label);
|
||||
}
|
||||
|
||||
public async $onContentChange(resourceComponents: UriComponents, viewType: string): Promise<void> {
|
||||
const model = await this.getCustomEditorModel(resourceComponents, viewType);
|
||||
model.changeContent();
|
||||
}
|
||||
|
||||
private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) {
|
||||
const disposables = new DisposableStore();
|
||||
|
||||
|
@ -540,6 +540,15 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
|||
return this._webviewInputs.getInputForHandle(handle);
|
||||
}
|
||||
|
||||
private async getCustomEditorModel(resourceComponents: UriComponents, viewType: string) {
|
||||
const resource = URI.revive(resourceComponents);
|
||||
const model = await this._customEditorService.models.get(resource, viewType);
|
||||
if (!model || !(model instanceof MainThreadCustomEditorModel)) {
|
||||
throw new Error('Could not find model for webview editor');
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private static getWebviewResolvedFailedContent(viewType: string) {
|
||||
return `<!DOCTYPE html>
|
||||
<html>
|
||||
|
@ -597,11 +606,12 @@ namespace HotExitState {
|
|||
class MainThreadCustomEditorModel extends Disposable implements ICustomEditorModel, IWorkingCopy {
|
||||
|
||||
private _hotExitState: HotExitState.State = HotExitState.Allowed;
|
||||
private _fromBackup: boolean = false;
|
||||
private readonly _fromBackup: boolean = false;
|
||||
|
||||
private _currentEditIndex: number = -1;
|
||||
private _savePoint: number = -1;
|
||||
private readonly _edits: Array<number> = [];
|
||||
private _isDirtyFromContentChange = false;
|
||||
|
||||
private _ongoingSave?: CancelablePromise<void>;
|
||||
|
||||
|
@ -676,6 +686,9 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
if (this._isDirtyFromContentChange) {
|
||||
return true;
|
||||
}
|
||||
if (this._edits.length > 0) {
|
||||
return this._savePoint !== this._currentEditIndex;
|
||||
}
|
||||
|
@ -717,6 +730,12 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||
});
|
||||
}
|
||||
|
||||
public changeContent() {
|
||||
this.change(() => {
|
||||
this._isDirtyFromContentChange = true;
|
||||
});
|
||||
}
|
||||
|
||||
private async undo(): Promise<void> {
|
||||
if (!this._editable) {
|
||||
return;
|
||||
|
@ -779,12 +798,13 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||
return;
|
||||
}
|
||||
|
||||
if (this._currentEditIndex === this._savePoint) {
|
||||
if (this._currentEditIndex === this._savePoint && !this._isDirtyFromContentChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._proxy.$revert(this._editorResource, this.viewType, CancellationToken.None);
|
||||
this.change(() => {
|
||||
this._isDirtyFromContentChange = false;
|
||||
this._currentEditIndex = this._savePoint;
|
||||
this.spliceEdits();
|
||||
});
|
||||
|
@ -805,6 +825,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
|||
this._ongoingSave = savePromise;
|
||||
|
||||
this.change(() => {
|
||||
this._isDirtyFromContentChange = false;
|
||||
this._savePoint = this._currentEditIndex;
|
||||
});
|
||||
|
||||
|
|
|
@ -615,6 +615,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
|
|||
$unregisterEditorProvider(viewType: string): void;
|
||||
|
||||
$onDidEdit(resource: UriComponents, viewType: string, editId: number, label: string | undefined): void;
|
||||
$onContentChange(resource: UriComponents, viewType: string): void;
|
||||
}
|
||||
|
||||
export interface WebviewPanelViewStateData {
|
||||
|
|
|
@ -570,9 +570,13 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
const documentEntry = this._documents.add(viewType, document);
|
||||
|
||||
if (this.isEditable(document)) {
|
||||
document.onDidEdit(e => {
|
||||
const editId = documentEntry.addEdit(e);
|
||||
this._proxy.$onDidEdit(document.uri, viewType, editId, e.label);
|
||||
document.onDidChange(e => {
|
||||
if (isEditEvent(e)) {
|
||||
const editId = documentEntry.addEdit(e);
|
||||
this._proxy.$onDidEdit(document.uri, viewType, editId, e.label);
|
||||
} else {
|
||||
this._proxy.$onContentChange(document.uri, viewType);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -708,7 +712,7 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape {
|
|||
}
|
||||
|
||||
private isEditable(document: vscode.CustomDocument): document is vscode.EditableCustomDocument {
|
||||
return !!(document as vscode.EditableCustomDocument).onDidEdit;
|
||||
return !!(document as vscode.EditableCustomDocument).onDidChange;
|
||||
}
|
||||
|
||||
private getEditableCustomDocument(viewType: string, resource: UriComponents): vscode.EditableCustomDocument {
|
||||
|
@ -744,3 +748,8 @@ function getDefaultLocalResourceRoots(
|
|||
extension.extensionLocation,
|
||||
];
|
||||
}
|
||||
|
||||
function isEditEvent(e: vscode.CustomDocumentContentChangeEvent | vscode.CustomDocumentEditEvent): e is vscode.CustomDocumentEditEvent {
|
||||
return typeof (e as vscode.CustomDocumentEditEvent).undo === 'function'
|
||||
&& typeof (e as vscode.CustomDocumentEditEvent).redo === 'function';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue