diff --git a/builtins.ts b/builtins.ts index e43706a..ec79209 100644 --- a/builtins.ts +++ b/builtins.ts @@ -5,10 +5,10 @@ import { } from "./window.ts"; import { font } from "./font.ts"; // import { keyDown, keyPressed, keyReleased } from "./keyboard.ts"; -import { addToContext } from "./runcode.ts"; +import { addToContext, runCode } from "./runcode.ts"; import { resetRepl } from "./repl.ts"; import { COLOR } from "./colors.ts"; -import { getSheet } from "./sheet.ts"; +import { getSheet, getCodeSheet } from "./sheet.ts"; export const drawSprite = (x: number, y: number, spr: number) => { const {sheet_type, value: sprites} = getSheet(2); @@ -43,6 +43,9 @@ const faux = { // key_down: keyDown, // key_pressed: keyPressed, // key_released: keyReleased, + code: (n: number) => { + return runCode(getCodeSheet(n)); + }, log: console.log, JSON: JSON, }; diff --git a/codetab.ts b/codetab.ts index bec6745..44520cf 100644 --- a/codetab.ts +++ b/codetab.ts @@ -2,11 +2,49 @@ import { clearScreen, fillRect } from "./window.ts"; import { fontWidth, fontHeight } from "./font.ts"; import { drawText } from "./builtins.ts"; import { COLOR } from "./colors.ts"; -import {getSheet, setSheet} from "./sheet.ts"; +import {getCodeSheet, setSheet} from "./sheet.ts"; import { K, ctrlKeyDown, getKeyboardString, keyPressed, shiftKeyDown } from "./keyboard.ts"; import { clipboard, tokenize } from "./deps.ts"; +const historyDebounceFrames = 20; + const state = { + history: [{code: getCodeSheet(0), anchor: 0, focus: 0}], + historyDebounce: 0, + historyIndex: 0, + undo() { + if (this.historyIndex === this.history.length && this.historyDebounce > 0) { + this.snapshot(); + } + if (this.historyIndex > 0) { + this.historyIndex -= 1; + const snap = this.history[this.historyIndex]; + this.code = snap.code; + this.setSelection(snap.anchor, snap.focus); + } + }, + redo() { + if (this.historyIndex < this.history.length-1) { + this.historyIndex += 1; + const snap = this.history[this.historyIndex]; + this.code = snap.code; + this.setSelection(snap.anchor, snap.focus); + } + }, + snapshot() { + this.history.push({ + code: this.code, + anchor: this.anchor, + focus: this.focus, + }); + }, + startSnapping() { + this.historyIndex += 1; + if (this.history.length > this.historyIndex) { + this.history.length = this.historyIndex; + } + this.historyDebounce = historyDebounceFrames; + }, scrollX: 0, scrollY: 0, anchor: 0, @@ -42,6 +80,7 @@ const state = { const {code, anchor, focus} = this; this.code = code.slice(0, Math.min(anchor, focus)) + text + code.slice(Math.max(anchor, focus)); this.setSelection(Math.min(anchor, focus) + text.length); + this.startSnapping(); }, indent(indentString: string) { const lines = this.code.split("\n"); @@ -55,6 +94,7 @@ const state = { }); this.code = newLines.join("\n"); this.setSelection({x: anchorX+1, y: anchorY}, {x: focusX+1, y: focusY}); + this.startSnapping(); }, outdent(outdentRegex: RegExp) { const lines = this.code.split("\n"); @@ -69,6 +109,7 @@ const state = { }); this.code = newLines.join("\n"); this.setSelection({x: Math.max(0,anchorX-1), y: anchorY}, {x: Math.max(0,focusX-1), y: focusY}); + this.startSnapping(); }, backspace() { const {code, focus} = this; @@ -76,6 +117,7 @@ const state = { if (focus > 0) { this.code = code.slice(0, focus-1) + code.slice(focus); this.setSelection(focus-1); + this.startSnapping(); } } else { this.insertText(""); @@ -86,6 +128,7 @@ const state = { if (this.isCollapsed()) { if (focus < code.length) { this.code = code.slice(0, focus) + code.slice(1+focus); + this.startSnapping(); } } else { this.insertText(""); @@ -101,7 +144,6 @@ const state = { this.insertText(""); }, async paste() { - this.insertText(await clipboard.readText()); }, scrollToCursor() { @@ -122,11 +164,7 @@ const state = { } }, get code() { - const {sheet_type, value} = getSheet(0); - if (sheet_type !== "code") { - throw "Trying to run a non-code sheet as code." - } - return value; + return getCodeSheet(0); }, set code(val) { setSheet(0, "code", val); @@ -347,12 +385,18 @@ const drawCodeField = (code: string, x: number, y: number, w: number, h: number) const update = async () => { const { focus, focusX, focusY} = state; + if (state.historyDebounce > 0) { + state.historyDebounce -= 1; + if (state.historyDebounce <= 0) { + state.snapshot(); + } + } + const keyboardString = getKeyboardString(); if (keyboardString) { state.insertText(keyboardString); state.scrollToCursor(); } - // TODO: Handle ctrl-Z // TODO: Make ctrl-/ do commenting out (take inspiration from tab) if (keyPressed(K.ENTER)) { @@ -414,12 +458,25 @@ const update = async () => { } if (keyPressed("C") && ctrlKeyDown()) { await state.copy(); + state.scrollToCursor(); } if (keyPressed("X") && ctrlKeyDown()) { await state.cut(); + state.scrollToCursor(); } if (keyPressed("V") && ctrlKeyDown()) { await state.paste(); + state.scrollToCursor(); + } + if (keyPressed("Z") && ctrlKeyDown()) { + if (shiftKeyDown()) { + state.redo(); + } else { + state.undo(); + } + } + if (keyPressed("Y") && ctrlKeyDown()) { + state.redo(); } } diff --git a/index.ts b/index.ts index acb29f8..11ec162 100644 --- a/index.ts +++ b/index.ts @@ -3,7 +3,8 @@ import { frame, clearScreen, } from "./window.ts"; -import { codeSheet } from "./sheet.ts"; +import { runCode } from "./runcode.ts"; +import { getCodeSheet } from "./sheet.ts"; import { refreshKeyboard, keyPressed, K } from "./keyboard.ts"; import { repl, resetRepl } from "./repl.ts"; import { addToContext } from "./runcode.ts"; @@ -17,7 +18,7 @@ let mode: "play" | "edit" | "repl" = "repl"; addToContext("play", () => { mode = "play"; - game = codeSheet(0); + game = runCode(getCodeSheet(0)); game.init(); }); diff --git a/sheet.ts b/sheet.ts index 3995fd2..08704dc 100644 --- a/sheet.ts +++ b/sheet.ts @@ -1,5 +1,5 @@ import { getCart } from "./cart.ts"; -import { runCode, addToContext } from "./runcode.ts"; +// import { runCode, addToContext } from "./runcode.ts"; // "code" | "spritesheet" | "map" | "sfx" | "patterns" | "fonts" export type Sheet = { @@ -20,12 +20,20 @@ export const setSheet = (n: number, type: SheetType, value: any) => { return getCart()[n] = {sheet_type: type, value}; } -export const codeSheet = (sheet: number) => { +export const getCodeSheet = (sheet: number) => { const {sheet_type, value} = getSheet(sheet); if (sheet_type !== "code") { - throw "Trying to run a non-code sheet as code." + throw "Trying to use a non-code sheet as code." } - return runCode(value); + return value; } -addToContext("code_sheet", codeSheet); \ No newline at end of file +export const getSpriteSheet = (sheet: number) => { + const {sheet_type, value} = getSheet(sheet); + if (sheet_type !== "spritesheet") { + throw "Trying to use a non-sprite sheet as a spritesheet." + } + return value; +} + +// addToContext("code", codeSheet); \ No newline at end of file