Add undo-redo
This commit is contained in:
parent
9685568f90
commit
3ad23f3a91
@ -5,10 +5,10 @@ import {
|
|||||||
} from "./window.ts";
|
} from "./window.ts";
|
||||||
import { font } from "./font.ts";
|
import { font } from "./font.ts";
|
||||||
// import { keyDown, keyPressed, keyReleased } from "./keyboard.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 { resetRepl } from "./repl.ts";
|
||||||
import { COLOR } from "./colors.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) => {
|
export const drawSprite = (x: number, y: number, spr: number) => {
|
||||||
const {sheet_type, value: sprites} = getSheet(2);
|
const {sheet_type, value: sprites} = getSheet(2);
|
||||||
@ -43,6 +43,9 @@ const faux = {
|
|||||||
// key_down: keyDown,
|
// key_down: keyDown,
|
||||||
// key_pressed: keyPressed,
|
// key_pressed: keyPressed,
|
||||||
// key_released: keyReleased,
|
// key_released: keyReleased,
|
||||||
|
code: (n: number) => {
|
||||||
|
return runCode(getCodeSheet(n));
|
||||||
|
},
|
||||||
log: console.log,
|
log: console.log,
|
||||||
JSON: JSON,
|
JSON: JSON,
|
||||||
};
|
};
|
||||||
|
73
codetab.ts
73
codetab.ts
@ -2,11 +2,49 @@ import { clearScreen, fillRect } from "./window.ts";
|
|||||||
import { fontWidth, fontHeight } from "./font.ts";
|
import { fontWidth, fontHeight } from "./font.ts";
|
||||||
import { drawText } from "./builtins.ts";
|
import { drawText } from "./builtins.ts";
|
||||||
import { COLOR } from "./colors.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 { K, ctrlKeyDown, getKeyboardString, keyPressed, shiftKeyDown } from "./keyboard.ts";
|
||||||
import { clipboard, tokenize } from "./deps.ts";
|
import { clipboard, tokenize } from "./deps.ts";
|
||||||
|
|
||||||
|
const historyDebounceFrames = 20;
|
||||||
|
|
||||||
const state = {
|
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,
|
scrollX: 0,
|
||||||
scrollY: 0,
|
scrollY: 0,
|
||||||
anchor: 0,
|
anchor: 0,
|
||||||
@ -42,6 +80,7 @@ const state = {
|
|||||||
const {code, anchor, focus} = this;
|
const {code, anchor, focus} = this;
|
||||||
this.code = code.slice(0, Math.min(anchor, focus)) + text + code.slice(Math.max(anchor, focus));
|
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.setSelection(Math.min(anchor, focus) + text.length);
|
||||||
|
this.startSnapping();
|
||||||
},
|
},
|
||||||
indent(indentString: string) {
|
indent(indentString: string) {
|
||||||
const lines = this.code.split("\n");
|
const lines = this.code.split("\n");
|
||||||
@ -55,6 +94,7 @@ const state = {
|
|||||||
});
|
});
|
||||||
this.code = newLines.join("\n");
|
this.code = newLines.join("\n");
|
||||||
this.setSelection({x: anchorX+1, y: anchorY}, {x: focusX+1, y: focusY});
|
this.setSelection({x: anchorX+1, y: anchorY}, {x: focusX+1, y: focusY});
|
||||||
|
this.startSnapping();
|
||||||
},
|
},
|
||||||
outdent(outdentRegex: RegExp) {
|
outdent(outdentRegex: RegExp) {
|
||||||
const lines = this.code.split("\n");
|
const lines = this.code.split("\n");
|
||||||
@ -69,6 +109,7 @@ const state = {
|
|||||||
});
|
});
|
||||||
this.code = newLines.join("\n");
|
this.code = newLines.join("\n");
|
||||||
this.setSelection({x: Math.max(0,anchorX-1), y: anchorY}, {x: Math.max(0,focusX-1), y: focusY});
|
this.setSelection({x: Math.max(0,anchorX-1), y: anchorY}, {x: Math.max(0,focusX-1), y: focusY});
|
||||||
|
this.startSnapping();
|
||||||
},
|
},
|
||||||
backspace() {
|
backspace() {
|
||||||
const {code, focus} = this;
|
const {code, focus} = this;
|
||||||
@ -76,6 +117,7 @@ const state = {
|
|||||||
if (focus > 0) {
|
if (focus > 0) {
|
||||||
this.code = code.slice(0, focus-1) + code.slice(focus);
|
this.code = code.slice(0, focus-1) + code.slice(focus);
|
||||||
this.setSelection(focus-1);
|
this.setSelection(focus-1);
|
||||||
|
this.startSnapping();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.insertText("");
|
this.insertText("");
|
||||||
@ -86,6 +128,7 @@ const state = {
|
|||||||
if (this.isCollapsed()) {
|
if (this.isCollapsed()) {
|
||||||
if (focus < code.length) {
|
if (focus < code.length) {
|
||||||
this.code = code.slice(0, focus) + code.slice(1+focus);
|
this.code = code.slice(0, focus) + code.slice(1+focus);
|
||||||
|
this.startSnapping();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.insertText("");
|
this.insertText("");
|
||||||
@ -101,7 +144,6 @@ const state = {
|
|||||||
this.insertText("");
|
this.insertText("");
|
||||||
},
|
},
|
||||||
async paste() {
|
async paste() {
|
||||||
|
|
||||||
this.insertText(await clipboard.readText());
|
this.insertText(await clipboard.readText());
|
||||||
},
|
},
|
||||||
scrollToCursor() {
|
scrollToCursor() {
|
||||||
@ -122,11 +164,7 @@ const state = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
get code() {
|
get code() {
|
||||||
const {sheet_type, value} = getSheet(0);
|
return getCodeSheet(0);
|
||||||
if (sheet_type !== "code") {
|
|
||||||
throw "Trying to run a non-code sheet as code."
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
},
|
},
|
||||||
set code(val) {
|
set code(val) {
|
||||||
setSheet(0, "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 update = async () => {
|
||||||
const { focus, focusX, focusY} = state;
|
const { focus, focusX, focusY} = state;
|
||||||
|
if (state.historyDebounce > 0) {
|
||||||
|
state.historyDebounce -= 1;
|
||||||
|
if (state.historyDebounce <= 0) {
|
||||||
|
state.snapshot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const keyboardString = getKeyboardString();
|
const keyboardString = getKeyboardString();
|
||||||
if (keyboardString) {
|
if (keyboardString) {
|
||||||
state.insertText(keyboardString);
|
state.insertText(keyboardString);
|
||||||
state.scrollToCursor();
|
state.scrollToCursor();
|
||||||
}
|
}
|
||||||
// TODO: Handle ctrl-Z
|
|
||||||
// TODO: Make ctrl-/ do commenting out (take inspiration from tab)
|
// TODO: Make ctrl-/ do commenting out (take inspiration from tab)
|
||||||
|
|
||||||
if (keyPressed(K.ENTER)) {
|
if (keyPressed(K.ENTER)) {
|
||||||
@ -414,12 +458,25 @@ const update = async () => {
|
|||||||
}
|
}
|
||||||
if (keyPressed("C") && ctrlKeyDown()) {
|
if (keyPressed("C") && ctrlKeyDown()) {
|
||||||
await state.copy();
|
await state.copy();
|
||||||
|
state.scrollToCursor();
|
||||||
}
|
}
|
||||||
if (keyPressed("X") && ctrlKeyDown()) {
|
if (keyPressed("X") && ctrlKeyDown()) {
|
||||||
await state.cut();
|
await state.cut();
|
||||||
|
state.scrollToCursor();
|
||||||
}
|
}
|
||||||
if (keyPressed("V") && ctrlKeyDown()) {
|
if (keyPressed("V") && ctrlKeyDown()) {
|
||||||
await state.paste();
|
await state.paste();
|
||||||
|
state.scrollToCursor();
|
||||||
|
}
|
||||||
|
if (keyPressed("Z") && ctrlKeyDown()) {
|
||||||
|
if (shiftKeyDown()) {
|
||||||
|
state.redo();
|
||||||
|
} else {
|
||||||
|
state.undo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keyPressed("Y") && ctrlKeyDown()) {
|
||||||
|
state.redo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
5
index.ts
5
index.ts
@ -3,7 +3,8 @@ import {
|
|||||||
frame,
|
frame,
|
||||||
clearScreen,
|
clearScreen,
|
||||||
} from "./window.ts";
|
} 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 { refreshKeyboard, keyPressed, K } from "./keyboard.ts";
|
||||||
import { repl, resetRepl } from "./repl.ts";
|
import { repl, resetRepl } from "./repl.ts";
|
||||||
import { addToContext } from "./runcode.ts";
|
import { addToContext } from "./runcode.ts";
|
||||||
@ -17,7 +18,7 @@ let mode: "play" | "edit" | "repl" = "repl";
|
|||||||
|
|
||||||
addToContext("play", () => {
|
addToContext("play", () => {
|
||||||
mode = "play";
|
mode = "play";
|
||||||
game = codeSheet(0);
|
game = runCode(getCodeSheet(0));
|
||||||
game.init();
|
game.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
18
sheet.ts
18
sheet.ts
@ -1,5 +1,5 @@
|
|||||||
import { getCart } from "./cart.ts";
|
import { getCart } from "./cart.ts";
|
||||||
import { runCode, addToContext } from "./runcode.ts";
|
// import { runCode, addToContext } from "./runcode.ts";
|
||||||
|
|
||||||
// "code" | "spritesheet" | "map" | "sfx" | "patterns" | "fonts"
|
// "code" | "spritesheet" | "map" | "sfx" | "patterns" | "fonts"
|
||||||
export type Sheet = {
|
export type Sheet = {
|
||||||
@ -20,12 +20,20 @@ export const setSheet = (n: number, type: SheetType, value: any) => {
|
|||||||
return getCart()[n] = {sheet_type: type, value};
|
return getCart()[n] = {sheet_type: type, value};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const codeSheet = (sheet: number) => {
|
export const getCodeSheet = (sheet: number) => {
|
||||||
const {sheet_type, value} = getSheet(sheet);
|
const {sheet_type, value} = getSheet(sheet);
|
||||||
if (sheet_type !== "code") {
|
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);
|
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);
|
Loading…
x
Reference in New Issue
Block a user