add file saving and dirty tracking
This commit is contained in:
parent
49cbd4ac35
commit
ec40c759d3
|
|
@ -63,9 +63,16 @@ export class Editor {
|
||||||
...searchKeymap,
|
...searchKeymap,
|
||||||
{ key: "Mod-z", run: () => undo(file.target) },
|
{ key: "Mod-z", run: () => undo(file.target) },
|
||||||
{ key: "Mod-shift-z", run: () => redo(file.target) },
|
{ key: "Mod-shift-z", run: () => redo(file.target) },
|
||||||
|
{
|
||||||
|
key: "Ctrl-s",
|
||||||
|
run: () => {
|
||||||
|
file.saveFile();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
this.view = new EditorView({
|
this.view = new EditorView({
|
||||||
doc: file.rootState.doc,
|
doc: file.rootState.val.doc,
|
||||||
dispatch: (trs) => this.dispatch(trs),
|
dispatch: (trs) => this.dispatch(trs),
|
||||||
extensions: [
|
extensions: [
|
||||||
oneDark,
|
oneDark,
|
||||||
|
|
@ -92,7 +99,7 @@ export class Editor {
|
||||||
});
|
});
|
||||||
const language = LanguageDescription.matchFilename(
|
const language = LanguageDescription.matchFilename(
|
||||||
languages,
|
languages,
|
||||||
file.filePath,
|
file.filePath.val,
|
||||||
)
|
)
|
||||||
?.load()
|
?.load()
|
||||||
.then((Lang) => {
|
.then((Lang) => {
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,12 @@ const EditorWrapper = (editor: any, del: any, k: any) =>
|
||||||
{ class: "flex flex-col" },
|
{ class: "flex flex-col" },
|
||||||
v.div(
|
v.div(
|
||||||
{ class: "flex" },
|
{ class: "flex" },
|
||||||
v.span({ class: "mx-1 flex-1" }, "Editor " + k),
|
v.span(
|
||||||
|
{ class: "mx-1 flex-1" },
|
||||||
|
() =>
|
||||||
|
editor.val.file.filePath.val +
|
||||||
|
(editor.val.file.isDirty() ? "*" : ""),
|
||||||
|
),
|
||||||
u.InlineButton(del, "Close", "❌"),
|
u.InlineButton(del, "Close", "❌"),
|
||||||
),
|
),
|
||||||
v.div({ class: "flex-auto h-4" }, editor.val.dom),
|
v.div({ class: "flex-auto h-4" }, editor.val.dom),
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,25 @@ import {
|
||||||
} from "@codemirror/state";
|
} from "@codemirror/state";
|
||||||
import { history } from "@codemirror/commands";
|
import { history } from "@codemirror/commands";
|
||||||
import { Editor } from "./editor";
|
import { Editor } from "./editor";
|
||||||
|
import { State } from "vanjs-core";
|
||||||
|
import van from "vanjs-core";
|
||||||
|
|
||||||
const openFiles: { [path: string]: OpenFile } = {};
|
const openFiles: { [path: string]: OpenFile } = {};
|
||||||
|
|
||||||
export class OpenFile {
|
export class OpenFile {
|
||||||
filePath: string;
|
filePath: State<string>;
|
||||||
editors: Editor[];
|
editors: Editor[];
|
||||||
rootState: EditorState;
|
rootState: State<EditorState>;
|
||||||
lastSaved?: Text;
|
lastSaved?: State<Text>;
|
||||||
|
|
||||||
constructor(cfg: EditorStateConfig) {
|
constructor(cfg: EditorStateConfig) {
|
||||||
this.filePath = null;
|
this.filePath = van.state(null);
|
||||||
this.editors = [];
|
this.editors = [];
|
||||||
this.rootState = EditorState.create(cfg).update({
|
this.rootState = van.state(
|
||||||
effects: [StateEffect.appendConfig.of([history()])],
|
EditorState.create(cfg).update({
|
||||||
}).state;
|
effects: [StateEffect.appendConfig.of([history()])],
|
||||||
|
}).state,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async openFile(filePath?: string): Promise<OpenFile> {
|
static async openFile(filePath?: string): Promise<OpenFile> {
|
||||||
|
|
@ -30,24 +34,24 @@ export class OpenFile {
|
||||||
}
|
}
|
||||||
const { content, path } = await window.electronAPI.readFile(filePath);
|
const { content, path } = await window.electronAPI.readFile(filePath);
|
||||||
const file = new OpenFile({ doc: content });
|
const file = new OpenFile({ doc: content });
|
||||||
file.lastSaved = file.rootState.doc;
|
file.lastSaved = van.state(file.rootState.val.doc);
|
||||||
file.setPath(path);
|
file.setPath(path);
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setPath(path: string) {
|
private setPath(path: string) {
|
||||||
delete openFiles[this.filePath];
|
delete openFiles[this.filePath?.val];
|
||||||
this.filePath = path;
|
this.filePath.val = path;
|
||||||
openFiles[path] = this;
|
openFiles[path] = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveFile() {
|
async saveFile() {
|
||||||
if (this.filePath) {
|
if (this.filePath) {
|
||||||
await window.electronAPI.saveFile(
|
await window.electronAPI.saveFile(
|
||||||
this.rootState.doc.toString(),
|
this.rootState.val.doc.toString(),
|
||||||
this.filePath,
|
this.filePath.val,
|
||||||
);
|
);
|
||||||
this.lastSaved = this.rootState.doc;
|
this.lastSaved.val = this.rootState.val.doc;
|
||||||
} else {
|
} else {
|
||||||
await this.saveAs();
|
await this.saveAs();
|
||||||
}
|
}
|
||||||
|
|
@ -55,11 +59,11 @@ export class OpenFile {
|
||||||
|
|
||||||
async saveAs(filePath?: string) {
|
async saveAs(filePath?: string) {
|
||||||
const { path } = await window.electronAPI.saveFile(
|
const { path } = await window.electronAPI.saveFile(
|
||||||
this.rootState.doc.toString(),
|
this.rootState.val.doc.toString(),
|
||||||
filePath,
|
filePath,
|
||||||
);
|
);
|
||||||
this.setPath(path);
|
this.setPath(path);
|
||||||
this.lastSaved = this.rootState.doc;
|
this.lastSaved.val = this.rootState.val.doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function to create and return a new EditorView for this file
|
// Function to create and return a new EditorView for this file
|
||||||
|
|
@ -70,7 +74,7 @@ export class OpenFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(trs: TransactionSpec, origin?: Editor) {
|
dispatch(trs: TransactionSpec, origin?: Editor) {
|
||||||
this.rootState = this.rootState.update(trs).state;
|
this.rootState.val = this.rootState.val.update(trs).state;
|
||||||
if (origin) {
|
if (origin) {
|
||||||
const es = this.editors.filter((e) => e !== origin);
|
const es = this.editors.filter((e) => e !== origin);
|
||||||
es.forEach((e) => e.dispatch(e.view.state.update(trs), true));
|
es.forEach((e) => e.dispatch(e.view.state.update(trs), true));
|
||||||
|
|
@ -84,12 +88,12 @@ export class OpenFile {
|
||||||
get target() {
|
get target() {
|
||||||
console.log("Getting target");
|
console.log("Getting target");
|
||||||
return {
|
return {
|
||||||
state: this.rootState,
|
state: this.rootState.val,
|
||||||
dispatch: (tr: TransactionSpec) => this.dispatch(tr),
|
dispatch: (tr: TransactionSpec) => this.dispatch(tr),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
isDirty(): boolean {
|
isDirty(): boolean {
|
||||||
return this.lastSaved !== this.rootState.doc;
|
return !this.lastSaved.val.eq(this.rootState.val.doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue