feat: on-demand section creation & 0.3.0.

This commit is contained in:
Shibo Lyu 2024-12-29 04:09:35 +08:00
parent dbfeb13c47
commit 5077995eb3
3 changed files with 87 additions and 28 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "@textplace/core", "name": "@textplace/core",
"version": "0.2.0", "version": "0.3.0",
"exports": "./mod.ts", "exports": "./mod.ts",
"imports": { "imports": {
"@deno/dnt": "jsr:@deno/dnt@^0.41.3" "@deno/dnt": "jsr:@deno/dnt@^0.41.3"

View file

@ -9,15 +9,7 @@ import { applyChange, createSection } from "./section.ts";
import type { BoardChange } from "../types/change.ts"; import type { BoardChange } from "../types/change.ts";
export function createBoard(config: BoardConfig): BoardData { export function createBoard(config: BoardConfig): BoardData {
const sections: SectionData[][] = Array(config.ySections) return { config, sections: [] };
.fill(0)
.map((_, sy) =>
Array(config.xSections)
.fill(0)
.map((_, sx) => createSection({ sx, sy }, config))
);
return { config, sections };
} }
export function locateSection( export function locateSection(
@ -30,9 +22,38 @@ export function locateSection(
}; };
} }
/**
* Get a section from board.
*
* If the section does not exist yet, it will be created and (optionally) added to `board`.
*/
export function getSectionOnBoard(
{ sx, sy }: SectionPosition,
board: BoardData,
options: {
/**
* Whether the section data is only used for reading.
*
* If `true`, this function will return an empty section configured with default values, but will not add it to the board data to save storage.
*/
readOnly: boolean;
} = { readOnly: false },
): SectionData {
let section: SectionData;
if (!board.sections[sy] && !options.readOnly) board.sections[sy] = [];
if (!board.sections[sy]?.[sx]) {
section = createSection({ sx, sy }, board.config);
if (!options.readOnly) board.sections[sy][sx] = section;
} else {
section = board.sections[sy][sx];
}
return section;
}
export function applyChangeOnBoard(change: BoardChange, board: BoardData) { export function applyChangeOnBoard(change: BoardChange, board: BoardData) {
const { sx, sy } = locateSection(change, board.config); const sPos = locateSection(change, board.config);
const section = board.sections[sy][sx]; const section = getSectionOnBoard(sPos, board);
applyChange(change, section); applyChange(change, section);
} }
@ -59,8 +80,8 @@ export function renderFullBoard(data: BoardData): FullBoard {
continue; continue;
} }
const { sx, sy } = locateSection({ x, y }, data.config); const sPos = locateSection({ x, y }, data.config);
const section = data.sections[sy][sx]; const section = getSectionOnBoard(sPos, data, { readOnly: true });
const xInSection = x % data.config.sectionWidth; const xInSection = x % data.config.sectionWidth;
const yInSection = y % data.config.sectionHeight; const yInSection = y % data.config.sectionHeight;

View file

@ -3,7 +3,11 @@ import {
assertEquals, assertEquals,
} from "https://deno.land/std@0.224.0/assert/mod.ts"; } from "https://deno.land/std@0.224.0/assert/mod.ts";
import { createBoard, renderFullBoard } from "../logic/board.ts"; import {
createBoard,
getSectionOnBoard,
renderFullBoard,
} from "../logic/board.ts";
import type { BoardData } from "../types/board.ts"; import type { BoardData } from "../types/board.ts";
import { checkFullBoard } from "./checkFullBoard.ts"; import { checkFullBoard } from "./checkFullBoard.ts";
import { locateSection } from "../logic/board.ts"; import { locateSection } from "../logic/board.ts";
@ -14,8 +18,8 @@ Deno.test("board", async (t) => {
await t.step("createBoard", () => { await t.step("createBoard", () => {
board = createBoard({ board = createBoard({
xSections: 2, xSections: 3,
ySections: 2, ySections: 3,
sectionWidth: 4, sectionWidth: 4,
sectionHeight: 3, sectionHeight: 3,
defaultCh: " ", defaultCh: " ",
@ -24,17 +28,8 @@ Deno.test("board", async (t) => {
defaultWidth: 1, defaultWidth: 1,
}); });
assertEquals(board.sections.length, 2); // Sections are created on demand.
assertEquals(board.sections[0].length, 2); assertEquals(board.sections.length, 0);
assertEquals(board.sections[0][0].offsetX, 0);
assertEquals(board.sections[0][0].offsetY, 0);
assertEquals(board.sections[0][1].offsetX, 4);
assertEquals(board.sections[0][1].offsetY, 0);
assertEquals(board.sections[1].length, 2);
assertEquals(board.sections[1][0].offsetX, 0);
assertEquals(board.sections[1][0].offsetY, 3);
assertEquals(board.sections[1][1].offsetX, 4);
assertEquals(board.sections[1][1].offsetY, 3);
}); });
await t.step("locateSection", () => { await t.step("locateSection", () => {
@ -73,10 +68,53 @@ Deno.test("board", async (t) => {
assertEquals(board.sections[1][1].ch[2][0], "嘛"); assertEquals(board.sections[1][1].ch[2][0], "嘛");
}); });
await t.step("getSectionOnBoard: existing section", () => {
assert(board);
const section = getSectionOnBoard({ sx: 1, sy: 1 }, board, {
readOnly: true,
});
assertEquals(section.ch[0][0], "嘛");
assertEquals(section.color[0][0], "F");
assertEquals(section.bgColor[0][0], "0");
assertEquals(section.width[0][0], 2);
});
await t.step("getSectionOnBoard: non-existing row", () => {
assert(board);
const section = getSectionOnBoard({ sx: 1, sy: 2 }, board, {
readOnly: true,
});
assertEquals(section.ch[0][0], " ");
assertEquals(section.color[0][0], "F");
assertEquals(section.bgColor[0][0], "0");
assertEquals(section.width[0][0], 1);
});
await t.step("getSectionOnBoard: non-existing section", () => {
assert(board);
const section = getSectionOnBoard({ sx: 2, sy: 1 }, board, {
readOnly: true,
});
assertEquals(section.ch[0][0], " ");
assertEquals(section.color[0][0], "F");
assertEquals(section.bgColor[0][0], "0");
assertEquals(section.width[0][0], 1);
});
await t.step("renderFullBoard", () => { await t.step("renderFullBoard", () => {
assert(board); assert(board);
const rendered = renderFullBoard(board); const rendered = renderFullBoard(board);
checkFullBoard(rendered); checkFullBoard(rendered);
}); });
await t.step("on-demand creation: only changed sections are saved", () => {
assert(board);
assertEquals(board.sections.length, 2);
assertEquals(board.sections[0].length, 2);
});
}); });