vscode-appwrite/src/ui/BaseEditor.ts
2021-04-30 02:49:02 -07:00

121 lines
5.9 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as fse from "fs-extra";
import * as path from "path";
import * as vscode from "vscode";
import { ext } from '../extensionVariables';
import { createTemporaryFile } from "./createTemporaryFile";
import { DialogResponses } from "./DialogResponses";
// tslint:disable-next-line:no-unsafe-any
export abstract class BaseEditor<ContextT> implements vscode.Disposable {
private fileMap: { [key: string]: [vscode.TextDocument, ContextT] } = {};
private ignoreSave: boolean = false;
constructor(private readonly showSavePromptKey: string) {}
public abstract getData(context: ContextT): Promise<string>;
public abstract updateData(context: ContextT, data: string): Promise<string>;
public abstract getFilename(context: ContextT): Promise<string>;
public abstract getResourceName(context: ContextT): Promise<string>;
public abstract getSaveConfirmationText(context: ContextT): Promise<string>;
public abstract getSize(context: ContextT): Promise<number>;
public async showEditor(context: ContextT): Promise<void> {
const fileName: string = await this.getFilename(context);
const resourceName: string = await this.getResourceName(context);
this.appendLineToOutput(`Opening ${resourceName}...`, { resourceName });
const localFilePath: string = await createTemporaryFile(fileName);
const document: vscode.TextDocument = await vscode.workspace.openTextDocument(localFilePath);
this.fileMap[localFilePath] = [document, context];
const data: string = await this.getData(context);
const textEditor: vscode.TextEditor = await vscode.window.showTextDocument(document);
await this.updateEditor(data, textEditor);
}
public async updateMatchingContext(doc: vscode.Uri): Promise<void> {
const filePath: string | undefined = Object.keys(this.fileMap).find((fsPath: string) => path.relative(doc.fsPath, fsPath) === "");
if (filePath) {
const [textDocument, context]: [vscode.TextDocument, ContextT] = this.fileMap[filePath];
await this.updateRemote(context, textDocument);
}
}
public async dispose(): Promise<void> {
Object.keys(this.fileMap).forEach(async (key: string) => await fse.remove(path.dirname(key)));
}
public async onDidSaveTextDocument(globalState: vscode.Memento, doc: vscode.TextDocument): Promise<void> {
const filePath: string | undefined = Object.keys(this.fileMap).find(
(fsPath: string) => path.relative(doc.uri.fsPath, fsPath) === ""
);
if (!this.ignoreSave && filePath) {
const context: ContextT = this.fileMap[filePath][1];
const showSaveWarning: boolean | undefined = vscode.workspace.getConfiguration().get(this.showSavePromptKey);
if (showSaveWarning) {
const message: string = await this.getSaveConfirmationText(context);
const result: vscode.MessageItem | undefined = await vscode.window.showWarningMessage(
message,
DialogResponses.upload,
DialogResponses.alwaysUpload,
DialogResponses.dontUpload
);
if (result === DialogResponses.alwaysUpload) {
await vscode.workspace.getConfiguration().update(this.showSavePromptKey, false, vscode.ConfigurationTarget.Global);
await globalState.update(this.showSavePromptKey, true);
}
}
await this.updateRemote(context, doc);
}
}
protected appendLineToOutput(value: string, options?: { resourceName?: string; date?: Date }): void {
ext.outputChannel?.appendLog(value, options);
ext.outputChannel?.show(true);
}
private async updateRemote(context: ContextT, doc: vscode.TextDocument): Promise<void> {
const filename: string = await this.getFilename(context);
const resourceName: string = await this.getResourceName(context);
this.appendLineToOutput(`Updating "${filename}" ...', filename)`, { resourceName });
const updatedData: string = await this.updateData(context, doc.getText());
this.appendLineToOutput(`Updated "${filename}".`, { resourceName });
if (doc.isClosed !== true) {
const visibleDocument: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.find((ed) => ed.document === doc);
if (visibleDocument) {
await this.updateEditor(updatedData, visibleDocument);
}
}
}
private async updateEditor(data: string, textEditor?: vscode.TextEditor): Promise<void> {
if (textEditor) {
await BaseEditor.writeToEditor(textEditor, data);
this.ignoreSave = true;
try {
await textEditor.document.save();
} finally {
this.ignoreSave = false;
}
}
}
// tslint:disable-next-line:member-ordering
private static async writeToEditor(editor: vscode.TextEditor, data: string): Promise<void> {
await editor.edit((editBuilder: vscode.TextEditorEdit) => {
if (editor.document.lineCount > 0) {
const lastLine: vscode.TextLine = editor.document.lineAt(editor.document.lineCount - 1);
editBuilder.delete(
new vscode.Range(
new vscode.Position(0, 0),
new vscode.Position(lastLine.range.start.line, lastLine.range.end.character)
)
);
}
editBuilder.insert(new vscode.Position(0, 0), data);
});
}
}