more cleanup

This commit is contained in:
sample-text-here 2021-02-03 19:30:46 -08:00
parent bd8ac12eb1
commit d992569d6f
11 changed files with 272 additions and 308 deletions

View file

@ -4,12 +4,7 @@ its not actually really an ide but i dont care
### using
press ctrl-alt-s to format file,
press ctrl-enter to run
you need a `return` to actually get a value from your program
ctrl-s, ctrl-o, and ctrl-shift-s do as expected
look at the menu items if you "need" a tutorial
### building

View file

@ -6,17 +6,18 @@ import {
MenuItem,
shell,
dialog,
Notification,
} from "electron";
import * as fs from "fs";
import * as path from "path";
import { generateMenu } from "./libs/menu";
import { generateMenu, generateRecents } from "./libs/menu";
import { parse } from "./libs/args";
import { paths, options, reload } from "./libs/options";
import { Bind } from "./libs/keybind";
import event from "./libs/events";
const args = parse();
const allowed = ["js", "json", "md", "txt", "mlog"];
const keys = [];
const sessions = [];
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require("electron-squirrel-startup")) app.quit();
@ -27,7 +28,9 @@ function regenMenu(): void {
new MenuItem({
label: "help",
role: "help",
click: (): void => showHelp(),
click: (): void => {
createWindow("help.html").setMenu(null);
},
})
);
@ -35,31 +38,48 @@ function regenMenu(): void {
while (keys.length > 0) keys.pop();
for (let category in options.keybinds) {
const binds = options.keybinds[category];
for (let bind in binds) {
if (!["run", "clear", "format", "showFile"].includes(bind)) continue;
keys.push({ bind: new Bind(binds[bind]), message: bind });
}
const binds = options.keybinds;
const override = { ...binds.files, ...binds.code };
for (let bind in override) {
keys.push({ bind: new Bind(override[bind]), message: bind });
}
if (!new Bind(binds.files.config).isValid()) {
new Notification({
title: "achievement unlocked",
body: "why did you do that",
}).show();
}
}
function createWindow(): BrowserWindow {
function createWindow(file: string, options = {}): BrowserWindow {
const win = new BrowserWindow({
height: 350,
width: 350,
show: false,
// TODO: make an icon that doesn't make my eyes bleed
// icon: path.join(__dirname, "assets", "icon.png"),
...options,
});
sessions.push(win);
win.loadFile(path.join(__dirname, "window", file));
win.once("ready-to-show", () => {
win.show();
});
return win;
}
function createSession(): BrowserWindow {
const win = createWindow("index.html", {
height: 400,
width: 600,
webPreferences: {
nodeIntegration: true,
enableRemoteModule: true,
},
show: false,
});
// and load the index.html of the app.
win.loadFile(path.join(__dirname, "window/index.html"));
win.once("ready-to-show", () => {
win.show();
});
win.webContents.on("before-input-event", (e, input) => {
@ -68,12 +88,18 @@ function createWindow(): BrowserWindow {
press.shift = input.shift;
press.alt = input.alt;
if (!press.isValid()) return;
for (const i of keys) {
for (let i of keys) {
if (press.isSame(i.bind)) {
e.preventDefault();
win.webContents.send("menu", i.message);
break;
if (i.message === "quit") {
win.close();
const index = sessions.indexOf(win);
if (index < 0) return;
sessions.splice(index, 1);
if (sessions.length <= 0) return;
}
return;
}
}
});
@ -81,61 +107,37 @@ function createWindow(): BrowserWindow {
return win;
}
function showHelp(): void {
const help = new BrowserWindow({
height: 350,
width: 350,
show: false,
});
help.loadFile(path.join(__dirname, "window/help.html"));
help.setMenu(null);
help.once("ready-to-show", () => {
help.show();
});
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on("ready", () => {
const win = createWindow();
reload();
regenMenu();
const win = createSession();
win.once("ready-to-show", () => {
if (args.file) {
// (ab)use openRecent
win.webContents.send("openRecent", args.file);
}
});
regenMenu();
});
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("activate", () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
app.on("window-all-closed", app.quit);
// TODO: only reload needed parts
// seperate event for a full reload
const reloadEv = event("reload");
const reloadEv = event("reload", true);
function reloadAll(): void {
reloadEv.fire();
reload();
regenMenu();
}
function reloadRecent(): void {
reload();
regenMenu();
}
fs.watchFile(paths.config, reloadAll);
event("shouldReload").addListener(reloadAll);
event("showFile").addListener((file) => shell.showItemInFolder(file[0]));
event("reload.force", true).addListener(reloadAll);
event("reload.recents", true).addListener(reloadRecent);
event("showFile", true).addListener((file) => shell.showItemInFolder(file));

View file

@ -8,7 +8,7 @@ export enum EventScope {
export class Event {
readonly name: string = "";
listeners: Array<Function> = [];
scope: EventScope = EventScope.global;
scope: EventScope = EventScope.process;
constructor(name: string) {
this.name = name;
@ -41,9 +41,10 @@ export class Event {
}
}
export default function eventsInterface(name: string) {
export default function eventsInterface(name: string, isGlobal = false) {
if (Object.prototype.hasOwnProperty.call(events, name)) return events[name];
const event = new Event(name);
if (isGlobal) event.scope = EventScope.global;
events[name] = event;
return event;
}

View file

@ -4,7 +4,7 @@ import { remote } from "electron";
import event from "../libs/events";
import { options, paths, save } from "./options";
const updateEv = event("updateRecent");
const updateEv = event("reload.recents", true);
export function touch(path: string): void {
const r = options.internal.recent;
if (r.indexOf(path) >= 0) r.splice(r.indexOf(path), 1);
@ -14,18 +14,6 @@ export function touch(path: string): void {
updateEv.fire();
}
export function saveSketch(value: string): void {
fs.writeFileSync(paths.sketch, value);
}
export function loadSketch(): string {
return fs.readFileSync(paths.sketch, "utf8");
}
export function backup(file: string): void {
fs.writeFileSync(options.backup, file);
}
export function saveFile(path: string, value: string): void {
fs.writeFileSync(path, value);
}
@ -34,13 +22,13 @@ export function openFile(path: string): string {
return fs.readFileSync(path, "utf8");
}
export function fileOpen(): string[] {
return remote.dialog.showOpenDialogSync({
export function fileOpen(): string {
return (remote.dialog.showOpenDialogSync({
title: "open file",
properties: ["openFile"],
filters: options.filters,
defaultPath: options.filesDir,
});
}) || [])[0];
}
export function fileSave(): string {

View file

@ -16,7 +16,7 @@ function call(message: string) {
return () => send("menu", message);
}
function generateRecents(fileNames: Array<string>): Menu {
export function generateRecents(fileNames: Array<string>): Menu {
const recents = new Menu();
for (const file of fileNames) {
recents.append(

View file

@ -93,13 +93,14 @@ const defaultOptions: Options = {
format: "ctrl-alt-s",
},
files: {
open: "ctrl-s",
save: "ctrl-o",
open: "ctrl-o",
save: "ctrl-s",
saveAs: "ctrl-shift-s",
openRecent: "ctrl-shift-o",
showFile: "ctrl-shift-e",
sketchpad: "ctrl-alt-o",
quit: "ctrl-q",
config: "ctrl-,",
},
},
internal: {
@ -129,6 +130,7 @@ const vm = new NodeVM({
export function reload(): void {
const newOpts: Options = deepCopy<Options>(defaultOptions);
try {
const json = JSON.parse(fs.readFileSync(paths.internal, "utf8"));
if (typeof json !== "object") throw "not object";
@ -144,7 +146,9 @@ export function reload(): void {
if (typeof result !== "object") throw "result not an object";
if (!result) throw "result is null";
assign(newOpts, result);
} catch {}
} catch (err) {
console.error(err);
}
const allFilters: Array<string> = [];
for (let i of newOpts.filters) {
@ -152,7 +156,7 @@ export function reload(): void {
}
newOpts.filters.push({ name: "all", extensions: allFilters });
if (!isOptions(options)) throw "invalid options";
if (!isOptions(newOpts)) throw "invalid options";
Object.assign(options, newOpts);
if (!fs.existsSync(options.filesDir)) {

View file

@ -5,7 +5,7 @@ export function deepCopyArray(arr: Array<any>) {
if (i instanceof Array) {
out.push(deepCopyArray(i));
} else {
out.push(deepCopy<Object>(i));
out.push(deepCopy(i));
}
} else {
out.push(i);
@ -21,7 +21,7 @@ export function deepCopy<T = Object>(obj: Object): T {
if (obj[i] instanceof Array) {
out[i] = deepCopyArray(obj[i]);
} else {
out[i] = deepCopy<Object>(obj[i]);
out[i] = deepCopy(obj[i]);
}
} else {
out[i] = obj[i];

View file

@ -77,6 +77,9 @@ export class Editor extends Element {
case "js":
session.setMode("ace/mode/javascript");
break;
case "ts":
session.setMode("ace/mode/typescript");
break;
case "json":
session.setMode("ace/mode/json");
break;

View file

@ -26,6 +26,7 @@ export class Popup extends Element {
super();
this.options = { ...defaultOpts, ...options };
const el = create("div", ["popup"], text);
el.style.display = "none";
if (this.options.dots) el.classList.add("dots");
parent.append(el);
this.element = el;

View file

@ -8,161 +8,85 @@ import { Bar } from "../ui/dragBar";
import { Popup } from "../ui/popup";
import { formatWithCursor } from "prettier";
import * as vm from "../libs/run";
import * as files from "../libs/files";
import { basename, extname } from "path";
import * as ts from "../libs/compile";
import event from "../libs/events";
import { paths, options, reload } from "../libs/options";
import { rebind } from "./scripts/editorKeybinds";
import { FileHandler } from "./scripts/fileHandler";
const main = document.getElementById("main");
const [edit, bar, consol] = [
new Editor(main),
new Bar(main, "leftRight"),
new Console(main),
];
rebind(edit);
const reloadPopup = new Popup(document.body, "reloaded!", { fade: 2000 });
const edit = new Editor(main);
const bar = new Bar(main, "leftRight");
const consol = new Console(main);
const reloadPopup = new Popup(document.body, "reloaded!", { fade: 1000 });
bar.element.style.gridArea = "resize";
rebind(edit);
let filePath = null,
ext = "js",
updated = false;
const files = new FileHandler(edit);
const ev = {
showFile: event("showFile"),
openedConfig: event("warn.config"),
reload: event("reload", true),
open: event("file.open"),
};
edit.listen("showSettingsMenu", "ctrl-,", () => {
openPath(paths.config);
ev.openedConfig.addListener(() => {
consol.createAppender(true, "#e6e155")("you are editing a config file");
});
const shouldReload = event("shouldReload");
event("reload").addListener(() => {
ev.reload.addListener(() => {
reloadPopup.show();
reload();
rebind(edit);
edit.listen("showSettingsMenu", options.keybinds.files.config, () => {
files.openPath(paths.config);
});
});
ev.open.addListener(() => {
files.updated = false;
updateTitle();
// ace editor bug
window.blur();
window.focus();
});
edit.editor.session.on("change", () => {
if (filePath === null) {
files.saveSketch(edit.editor.session.getValue());
} else {
files.backup(edit.editor.session.getValue());
}
updated = true;
files.backup();
files.updated = true;
updateTitle();
});
// let reopenIndex = 0,
// reopenTimeout = null;
// TODO: make file management less awful
function postOpen(newPath: string): void {
queueMicrotask(() => {
updated = false;
updateTitle();
});
filePath = newPath;
ext = extname(newPath || ".js").replace(/^\./, "");
if (newPath) edit.editor.session.setValue(files.openFile(newPath));
edit.mode(ext);
if (newPath === paths.config)
consol.createAppender(true, "#e6e155")("you are editing a config file");
}
function confirmOpen() {
if (updated && filePath && !window.confirm("your file isnt saved, continue?"))
return false;
return true;
}
function save(): void {
if (!filePath) filePath = files.fileSave();
if (!filePath) return;
updated = false;
updateTitle();
files.saveFile(filePath, edit.editor.session.getValue());
if (filePath === paths.config) shouldReload.fire();
files.touch(filePath);
}
function saveAs(): void {
const newPath = files.fileSave();
if (!filePath) return;
updated = false;
updateTitle();
files.saveFile(newPath, edit.editor.session.getValue());
}
function open(): void {
if (!confirmOpen()) return;
// reopenIndex--;
// if (reopenIndex < 0) {
// reopenIndex = -1;
// openPath((files.fileOpen() || [])[0], true);
// return;
// }
// const newPath = options.internal.recent[reopenIndex];
// if (newPath) {
// files.openFileKeepPath(newPath);
// postOpen(newPath);
// clearTimeout(reopenTimeout);
// reopenTimeout = setTimeout(() => {
// files.recentFile(newPath);
// reopenIndex = -1;
// }, 2000);
// return;
// }
openPath((files.fileOpen() || [])[0], true);
}
function openPath(newPath: string, force = false): void {
if (!newPath) return;
if (!force && !confirmOpen()) return;
postOpen(newPath);
if (filePath !== newPath) files.touch(newPath);
}
// TODO: cyclic open/close
function reopen(): void {
// reopenIndex++;
// if (reopenIndex >= options.maxRecent) {
// reopenIndex = options.maxRecent - 1;
// }
// const newPath = options.internal.recent[reopenIndex];
// if (newPath) {
// files.openFileKeepPath(newPath);
// postOpen(newPath);
// clearTimeout(reopenTimeout);
// reopenTimeout = setTimeout(() => {
// reopenIndex = -1;
// files.recentFile(newPath);
// }, 2000);
// }
const newPath = options.internal.recent[filePath ? 1 : 0];
if (newPath) openPath(newPath);
}
function openSketch(): void {
if (!confirmOpen()) return;
edit.editor.session.setValue(files.loadSketch());
postOpen(null);
}
function format(): void {
if (ext === "txt") return;
if (!["js", "ts", "json", "md"].includes(files.ext)) return;
const editor = edit.editor;
const cursor = editor.selection.getCursor();
const index = editor.session.doc.positionToIndex(cursor);
const value = editor.session.getValue();
let parser = null;
switch (files.ext) {
case "js":
parser = "babel";
break;
case "ts":
parser = "babel-typescript";
break;
case "json":
case "md":
parser = files.ext;
break;
}
if (!parser) return;
const result = formatWithCursor(value, {
cursorOffset: index,
parser: ext === "js" ? "babel" : ext,
parser,
});
editor.session.doc.setValue(result.formatted);
const position = editor.session.doc.indexToPosition(result.cursorOffset);
editor.clearSelection();
editor.moveCursorToPosition(position);
updateTitle();
}
bar.dragged = function (e): void {
@ -190,9 +114,8 @@ consol.run = (code): void => {
};
function run(): void {
const code = edit.editor.session.getValue();
console.log(ext);
switch (ext) {
const code = files.value;
switch (files.ext) {
case "js":
const res = vm.run(code);
consol[res.err ? "error" : "log"](res.value);
@ -210,13 +133,13 @@ function run(): void {
ipcRenderer.on("menu", (e, message) => {
switch (message) {
case "save":
save();
files.save();
break;
case "saveAs":
saveAs();
files.saveAs();
break;
case "open":
open();
files.open();
break;
case "format":
format();
@ -228,27 +151,29 @@ ipcRenderer.on("menu", (e, message) => {
consol.clear();
break;
case "reopen":
reopen();
// reopen();
break;
case "sketch":
openSketch();
files.openPath(paths.sketch);
break;
case "showFile":
if (filePath) ipcRenderer.send("showFile", filePath);
if (files.path) ev.showFile.fire(files.path);
break;
}
updateTitle();
});
ipcRenderer.on("openRecent", (e, path) => {
openPath(path);
files.openPath(path);
});
function updateTitle(): void {
let title = "jside";
if (filePath) {
title += " - " + basename(filePath) + (updated ? "*" : "");
if (files.path) {
title += " - " + files.name;
if (files.updated) title += "*";
}
document.title = title;
}
edit.editor.session.doc.setValue(files.loadSketch());
files.openPath(paths.sketch);

View file

@ -1,94 +1,139 @@
// TODO: clean up and use this file instead
/*
function postOpen(newPath: string): void {
queueMicrotask(() => {
updated = false;
updateTitle();
});
filePath = newPath;
ext = extname(newPath || ".js").replace(/^\./, "");
if (newPath) edit.editor.session.setValue(files.openFile(newPath));
edit.mode(ext);
if (newPath === paths.config)
consol.raw("you are editing a config file", "warn");
}
import * as files from "../../libs/files";
import { basename, extname } from "path";
import { paths, options } from "../../libs/options";
import event from "../../libs/events";
import { Editor } from "../../ui/editor";
function confirmOpen() {
if (updated && filePath && !window.confirm("your file isnt saved, continue?"))
const ev = {
open: event("file.open"),
save: event("file.save"),
saveAs: event("file.saveAs"),
reload: event("reload.force"),
openedConfig: event("warn.config"),
};
export class FileHandler {
path: string = null;
edit: Editor = null;
updated = false;
constructor(edit: Editor) {
this.edit = edit;
}
get ext() {
return extname(this.path).replace(/^\./, "");
}
get name() {
return basename(this.path);
}
get value() {
return this.edit.editor.session.getValue();
}
set value(val) {
this.edit.editor.session.setValue(val);
}
confirm() {
if (!this.updated) return true;
if (!this.path) return true;
if (window.confirm("your file isnt saved, continue?")) return true;
return false;
return true;
}
}
function save(): void {
if (!filePath) filePath = files.fileSave();
if (!filePath) return;
updated = false;
updateTitle();
files.saveFile(filePath, edit.editor.session.getValue());
if (filePath === paths.config) shouldReload.fire();
if(filePath !== newPath) files.recentFile(newPath);
}
openPath(path: string): void {
if (!path) return;
if (!this.confirm()) return;
this.path = path;
if (path) this.value = files.openFile(path);
this.edit.mode(this.ext || "js");
if (path === paths.config) ev.openedConfig.fire();
files.touch(path);
this.updated = false;
ev.open.fire(path);
}
function saveAs(): void {
const newPath = files.fileSave();
if (!filePath) return;
updated = false;
updateTitle();
files.saveFile(newPath, edit.editor.session.getValue());
}
open(): void {
if (!this.confirm()) return;
const oldPath = this.path;
this.path = null;
this.openPath(files.fileOpen());
if (!this.path) this.path = oldPath;
}
function open(): void {
if (!confirmOpen()) return;
// reopenIndex--;
// if (reopenIndex < 0) {
// reopenIndex = -1;
// openPath((files.fileOpen() || [])[0], true);
// return;
// }
// const newPath = options.internal.recent[reopenIndex];
// if (newPath) {
// files.openFileKeepPath(newPath);
// postOpen(newPath);
// clearTimeout(reopenTimeout);
// reopenTimeout = setTimeout(() => {
// files.recentFile(newPath);
// reopenIndex = -1;
// }, 2000);
// return;
// }
openPath((files.fileOpen() || [])[0], true);
}
save(): void {
if (!this.path) this.path = files.fileSave();
if (!this.path) return;
this.updated = false;
files.saveFile(this.path, this.value);
files.touch(this.path);
if (this.path === paths.config) ev.reload.fire();
ev.save.fire(this.path);
}
function openPath(newPath: string, force = false): void {
if (!newPath) return;
if (!force && !confirmOpen()) return;
postOpen(newPath);
if(filePath !== newPath) files.recentFile(newPath);
saveAs(): void {
const oldPath = this.path;
this.path = null;
this.save();
if (!this.path) {
this.path = oldPath;
} else {
ev.saveAs.fire(this.path);
}
}
backup() {
const path = this.path ? options.backup : paths.sketch;
files.saveFile(path, this.value);
}
}
// TODO: cyclic open/close
function reopen(): void {
// reopenIndex++;
// if (reopenIndex >= options.maxRecent) {
// reopenIndex = options.maxRecent - 1;
// }
// const newPath = options.internal.recent[reopenIndex];
// if (newPath) {
// files.openFileKeepPath(newPath);
// postOpen(newPath);
// clearTimeout(reopenTimeout);
// reopenTimeout = setTimeout(() => {
// reopenIndex = -1;
// files.recentFile(newPath);
// }, 2000);
// }
const newPath = options.internal.recent[filePath ? 1 : 0];
if (newPath) openPath(newPath);
}
// TODO: also ~~fix~~ re-add reopen, it's buggy
function openSketch(): void {
if (!confirmOpen()) return;
edit.editor.session.setValue(files.loadSketch());
postOpen(null);
}
*/
// let reopenIndex = 0,
// reopenTimeout = null;
// function open(): void {
// if (!confirmOpen()) return;
// reopenIndex--;
// if (reopenIndex < 0) {
// reopenIndex = -1;
// openPath((files.fileOpen() || [])[0], true);
// return;
// }
// const newPath = options.internal.recent[reopenIndex];
// if (newPath) {
// files.openFileKeepPath(newPath);
// postOpen(newPath);
// clearTimeout(reopenTimeout);
// reopenTimeout = setTimeout(() => {
// files.recentFile(newPath);
// reopenIndex = -1;
// }, 2000);
// return;
// }
// openPath((files.fileOpen() || [])[0], true);
// }
// function reopen(): void {
// reopenIndex++;
// if (reopenIndex >= options.maxRecent) {
// reopenIndex = options.maxRecent - 1;
// }
// const newPath = options.internal.recent[reopenIndex];
// if (newPath) {
// files.openFileKeepPath(newPath);
// postOpen(newPath);
// clearTimeout(reopenTimeout);
// reopenTimeout = setTimeout(() => {
// reopenIndex = -1;
// files.recentFile(newPath);
// }, 2000);
// }
// const newPath = options.internal.recent[filePath ? 1 : 0];
// if (newPath) openPath(newPath);
// }