Create Displayable interface, fix file save-as

This commit is contained in:
Quinten Kock 2025-11-14 00:43:37 +01:00
parent 55aac46630
commit 50ba297473
3 changed files with 41 additions and 23 deletions

View File

@ -1,9 +1,4 @@
import { import { Transaction, Compartment, Extension } from "@codemirror/state";
Transaction,
StateEffect,
Compartment,
Extension,
} from "@codemirror/state";
import { import {
EditorView, EditorView,
keymap, keymap,
@ -27,8 +22,10 @@ import {
} from "@codemirror/language"; } from "@codemirror/language";
import { languages } from "@codemirror/language-data"; import { languages } from "@codemirror/language-data";
import { highlightSelectionMatches, searchKeymap } from "@codemirror/search"; import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
import van from "vanjs-core";
import { OpenFile } from "./filestate"; import { OpenFile } from "./filestate";
import { Displayable } from "./editorgrid";
const fixedHeightEditor = EditorView.theme({ const fixedHeightEditor = EditorView.theme({
"&": { "&": {
@ -44,12 +41,13 @@ const fixedHeightEditor = EditorView.theme({
".cm-scroller": { overflow: "auto scroll" }, ".cm-scroller": { overflow: "auto scroll" },
}); });
export class Editor { export class Editor implements Displayable {
view: EditorView; view: EditorView;
file: OpenFile; file: OpenFile;
deleteFn?: () => void; deleteFn?: () => void;
private wordWrapCompartment = new Compartment(); private wordWrapCompartment = new Compartment();
private languageCompartment = new Compartment();
dispatch(tr: Transaction, inhibitSync = false) { dispatch(tr: Transaction, inhibitSync = false) {
this.view.update([tr]); this.view.update([tr]);
@ -95,6 +93,7 @@ export class Editor {
fixedHeightEditor, fixedHeightEditor,
kmap, kmap,
this.wordWrapCompartment.of(EditorView.lineWrapping), this.wordWrapCompartment.of(EditorView.lineWrapping),
this.languageCompartment.of([]),
lineNumbers(), lineNumbers(),
highlightSpecialChars(), highlightSpecialChars(),
foldGutter(), foldGutter(),
@ -113,12 +112,16 @@ export class Editor {
// lintKeymap, // lintKeymap,
], ],
}); });
van.derive(() => {
LanguageDescription.matchFilename(languages, file.filePath.val) LanguageDescription.matchFilename(languages, file.filePath.val)
?.load() ?.load()
.then((Lang) => { .then((Lang) => {
const eff = StateEffect.appendConfig.of(Lang); // const eff = StateEffect.appendConfig.of(Lang);
const eff = this.languageCompartment.reconfigure(Lang);
this.view.dispatch({ effects: [eff] }); this.view.dispatch({ effects: [eff] });
}); });
});
} }
get dom() { get dom() {
@ -130,9 +133,13 @@ export class Editor {
this.view.focus(); this.view.focus();
} }
title(): string {
return this.file.filePath.val + (this.file.isDirty() ? "*" : "");
}
changeWidth(increment: number) { changeWidth(increment: number) {
const w = parseInt(window.getComputedStyle(this.view.dom).width, 10); const w = parseInt(window.getComputedStyle(this.view.dom).width, 10);
this.view.dom.style.width = (w + increment) + 'px'; this.view.dom.style.width = w + increment + "px";
return true; return true;
} }

View File

@ -6,7 +6,18 @@ import { OpenFile } from "./filestate";
import * as u from "./utils"; import * as u from "./utils";
import { Editor } from "./editor"; import { Editor } from "./editor";
const EditorWrapper = (editor: State<Editor>, del: () => void, k: number) => { export interface Displayable {
setDeleteFunction(del: () => void): void;
title(): string;
close(): void;
dom: HTMLElement;
}
const EditorWrapper = (
editor: State<Displayable>,
del: () => void,
k: number,
) => {
// Set the delete function on the editor when it's created // Set the delete function on the editor when it's created
van.derive(() => { van.derive(() => {
if (editor.val) { if (editor.val) {
@ -18,12 +29,7 @@ const EditorWrapper = (editor: State<Editor>, del: () => void, k: number) => {
{ class: "flex flex-col" }, { class: "flex flex-col" },
v.div( v.div(
{ class: "flex" }, { class: "flex" },
v.span( v.span({ class: "mx-1 flex-1" }, () => editor.val.title()),
{ class: "mx-1 flex-1" },
() =>
editor.val.file.filePath.val +
(editor.val.file.isDirty() ? "*" : ""),
),
u.InlineButton(() => editor.val.close(), "Close", "❌"), u.InlineButton(() => editor.val.close(), "Close", "❌"),
), ),
v.div({ class: "flex-auto h-4" }, editor.val.dom), v.div({ class: "flex-auto h-4" }, editor.val.dom),
@ -55,6 +61,7 @@ const TabHeader = (tab: State<Editor[]>, del: () => void, k: number) =>
v.span({ class: "mx-1 flex-1" }, "Tab " + k), v.span({ class: "mx-1 flex-1" }, "Tab " + k),
u.InlineButton(del, "Close", "❌"), u.InlineButton(del, "Close", "❌"),
); );
const EditorGrid = (tab: State<Editor[]>, del: () => void, k: number) => { const EditorGrid = (tab: State<Editor[]>, del: () => void, k: number) => {
console.log("Rendering", tab.val, "with key", k); console.log("Rendering", tab.val, "with key", k);
const main = v.main({ const main = v.main({
@ -64,12 +71,15 @@ const EditorGrid = (tab: State<Editor[]>, del: () => void, k: number) => {
vanX.list(main, tab.val, EditorWrapper); vanX.list(main, tab.val, EditorWrapper);
return main; return main;
}; };
const TabBar = v.div({ class: "flex-none flex" }); const TabBar = v.div({ class: "flex-none flex" });
export const EditorTabs = v.div( export const EditorTabs = v.div(
{ {
class: "flex flex-col flex-auto min-w-4", class: "flex flex-col flex-auto min-w-4",
}, },
TabBar, TabBar,
); );
vanX.list(TabBar, editors, TabHeader); vanX.list(TabBar, editors, TabHeader);
vanX.list(EditorTabs, editors, EditorGrid); vanX.list(EditorTabs, editors, EditorGrid);

View File

@ -40,13 +40,14 @@ export class OpenFile {
} }
private setPath(path: string) { private setPath(path: string) {
delete openFiles[this.filePath?.val]; delete openFiles[this.filePath.val];
this.filePath.val = path; this.filePath.val = path;
openFiles[path] = this; openFiles[path] = this;
// TODO: what if openFiles[path] already exists?
} }
async saveFile() { async saveFile() {
if (this.filePath) { if (this.filePath.val) {
await window.electronAPI.saveFile( await window.electronAPI.saveFile(
this.rootState.val.doc.toString(), this.rootState.val.doc.toString(),
this.filePath.val, this.filePath.val,