121 lines
5.9 KiB
TypeScript
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);
|
|
});
|
|
}
|
|
}
|