Skip to content

Commit

Permalink
Working on hooking up custom editor saveAs
Browse files Browse the repository at this point in the history
  • Loading branch information
mjbvz committed Nov 25, 2019
1 parent d1fab46 commit ffe3749
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 7 deletions.
7 changes: 7 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,13 @@ declare module 'vscode' {
*/
save(): Thenable<void>;

/**
*
* @param resource Resource being saved.
* @param targetResource Location to save to.
*/
saveAs(resource: Uri, targetResource: Uri): Thenable<void>;

/**
* Event triggered by extensions to signal to VS Code that an edit has occurred.
*
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/browser/mainThreadWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
model.onUndo(edits => { this._proxy.$undoEdits(handle, edits); });
model.onRedo(edits => { this._proxy.$redoEdits(handle, edits); });
model.onWillSave(e => { e.waitUntil(this._proxy.$onSave(handle)); });
model.onWillSaveAs(e => { e.waitUntil(this._proxy.$onSaveAs(handle, e.resource.toJSON(), e.targetResource.toJSON())); });

webviewInput.onDispose(() => {
this._customEditorService.models.disposeModel(model);
Expand Down
4 changes: 4 additions & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,11 +590,15 @@ export interface ExtHostWebviewsShape {
$onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void;
$onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void;
$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: readonly any[]): void;
$redoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void;

$onSave(handle: WebviewPanelHandle): Promise<void>;
$onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise<void>;
}

export interface MainThreadUrlsShape extends IDisposable {
Expand Down
10 changes: 10 additions & 0 deletions src/vs/workbench/api/common/extHostWebview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,11 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
await assertIsDefined(this._capabilities).editingCapability?.save();
}


async _onSaveAs(resource: vscode.Uri, targetResource: vscode.Uri): Promise<void> {
await assertIsDefined(this._capabilities).editingCapability?.saveAs(resource, targetResource);
}

private assertNotDisposed() {
if (this._isDisposed) {
throw new Error('Webview is disposed');
Expand Down Expand Up @@ -462,6 +467,11 @@ export class ExtHostWebviews implements ExtHostWebviewsShape {
return panel?._onSave();
}

async $onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise<void> {
const panel = this.getWebviewPanel(handle);
return panel?._onSaveAs(URI.revive(resource), URI.revive(targetResource));
}

private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined {
return this._webviewPanels.get(handle);
}
Expand Down
41 changes: 38 additions & 3 deletions src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ import { UnownedDisposable } from 'vs/base/common/lifecycle';
import { basename } from 'vs/base/common/path';
import { isEqual } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, Verbosity } from 'vs/workbench/common/editor';
import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview';
import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput {

Expand All @@ -33,6 +35,8 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput {
@IWebviewWorkbenchService webviewWorkbenchService: IWebviewWorkbenchService,
@ILabelService private readonly labelService: ILabelService,
@ICustomEditorService private readonly customEditorService: ICustomEditorService,
@IEditorService private readonly editorService: IEditorService,
@IFileDialogService private readonly fileDialogService: IFileDialogService,
) {
super(id, viewType, '', webview, webviewWorkbenchService, lifecycleService);
this._editorResource = resource;
Expand Down Expand Up @@ -101,9 +105,32 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput {
return this._model ? this._model.save(options) : Promise.resolve(false);
}

public saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise<boolean> {
// TODO@matt implement properly (see TextEditorInput#saveAs())
return this._model ? this._model.save(options) : Promise.resolve(false);
public async saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise<boolean> {
if (!this._model) {
return false;
}

// Preserve view state by opening the editor first. In addition
// this allows the user to review the contents of the editor.
// let viewState: IEditorViewState | undefined = undefined;
// const editor = await this.editorService.openEditor(this, undefined, group);
// if (isTextEditor(editor)) {
// viewState = editor.getViewState();
// }

let dialogPath = this._editorResource;
// if (this._editorResource.scheme === Schemas.untitled) {
// dialogPath = this.suggestFileName(resource);
// }

const target = await this.promptForPath(this._editorResource, dialogPath, options?.availableFileSystems);
if (!target) {
return false; // save cancelled
}

await this._model.saveAs(this._editorResource, target, options);

return true;
}

public revert(options?: IRevertOptions): Promise<boolean> {
Expand All @@ -115,4 +142,12 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput {
this._register(this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
return await super.resolve();
}

protected async promptForPath(resource: URI, defaultUri: URI, availableFileSystems?: readonly string[]): Promise<URI | undefined> {

// Help user to find a name for the file by opening it first
await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true } });

return this.fileDialogService.pickFileToSave({});//this.getSaveDialogOptions(defaultUri, availableFileSystems));
}
}
15 changes: 14 additions & 1 deletion src/vs/workbench/contrib/customEditor/common/customEditor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,29 @@ export interface ICustomEditorModelManager {
disposeModel(model: ICustomEditorModel): void;
}

export interface CustomEditorSaveEvent {
readonly resource: URI;
readonly waitUntil: (until: Promise<any>) => void;
}

export interface CustomEditorSaveAsEvent {
readonly resource: URI;
readonly targetResource: URI;
readonly waitUntil: (until: Promise<any>) => void;
}

export interface ICustomEditorModel extends IWorkingCopy {
readonly onUndo: Event<readonly CustomEditorEdit[]>;
readonly onRedo: Event<readonly CustomEditorEdit[]>;
readonly onWillSave: Event<{ waitUntil: (until: Promise<any>) => void }>;
readonly onWillSave: Event<CustomEditorSaveEvent>;
readonly onWillSaveAs: Event<CustomEditorSaveAsEvent>;

undo(): void;
redo(): void;
revert(options?: IRevertOptions): Promise<boolean>;

save(options?: ISaveOptions): Promise<boolean>;
saveAs(resource: URI, targetResource: URI, currentOptions?: ISaveOptions): Promise<boolean>;

makeEdit(data: string): void;
}
Expand Down
33 changes: 30 additions & 3 deletions src/vs/workbench/contrib/customEditor/common/customEditorModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { ICustomEditorModel, CustomEditorEdit } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { ICustomEditorModel, CustomEditorEdit, CustomEditorSaveAsEvent, CustomEditorSaveEvent } from 'vs/workbench/contrib/customEditor/common/customEditor';
import { WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor';

Expand Down Expand Up @@ -47,9 +47,12 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel
protected readonly _onRedo = this._register(new Emitter<readonly CustomEditorEdit[]>());
readonly onRedo = this._onRedo.event;

protected readonly _onWillSave = this._register(new Emitter<{ waitUntil: (until: Promise<any>) => void }>());
protected readonly _onWillSave = this._register(new Emitter<CustomEditorSaveEvent>());
readonly onWillSave = this._onWillSave.event;

protected readonly _onWillSaveAs = this._register(new Emitter<CustomEditorSaveAsEvent>());
readonly onWillSaveAs = this._onWillSaveAs.event;

public makeEdit(data: string): void {
this._edits.splice(this._currentEditIndex + 1, this._edits.length - this._currentEditIndex, data);
this._currentEditIndex = this._edits.length - 1;
Expand All @@ -62,7 +65,10 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel

public async save(_options?: ISaveOptions): Promise<boolean> {
const untils: Promise<any>[] = [];
const handler = { waitUntil: (until: Promise<any>) => untils.push(until) };
const handler: CustomEditorSaveEvent = {
resource: this._resource,
waitUntil: (until: Promise<any>) => untils.push(until)
};

try {
this._onWillSave.fire(handler);
Expand All @@ -77,6 +83,27 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel
return true;
}

public async saveAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise<boolean> {
const untils: Promise<any>[] = [];
const handler: CustomEditorSaveAsEvent = {
resource,
targetResource,
waitUntil: (until: Promise<any>) => untils.push(until)
};

try {
this._onWillSaveAs.fire(handler);
await Promise.all(untils);
} catch {
return false;
}

this._savePoint = this._currentEditIndex;
this.updateDirty();

return true;
}

public async revert(_options?: IRevertOptions) {
if (this._currentEditIndex === this._savePoint) {
return true;
Expand Down

0 comments on commit ffe3749

Please sign in to comment.