From 5077995eb37c3c302e2d407cf0b7169033b2441c Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Sun, 29 Dec 2024 04:09:35 +0800 Subject: [PATCH 1/9] feat: on-demand section creation & 0.3.0. --- deno.json | 2 +- logic/board.ts | 47 +++++++++++++++++++++++--------- tests/board.test.ts | 66 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 87 insertions(+), 28 deletions(-) diff --git a/deno.json b/deno.json index 9bd54fd..14c2394 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@textplace/core", - "version": "0.2.0", + "version": "0.3.0", "exports": "./mod.ts", "imports": { "@deno/dnt": "jsr:@deno/dnt@^0.41.3" diff --git a/logic/board.ts b/logic/board.ts index eeb8765..9dcb312 100644 --- a/logic/board.ts +++ b/logic/board.ts @@ -9,15 +9,7 @@ import { applyChange, createSection } from "./section.ts"; import type { BoardChange } from "../types/change.ts"; export function createBoard(config: BoardConfig): BoardData { - const sections: SectionData[][] = Array(config.ySections) - .fill(0) - .map((_, sy) => - Array(config.xSections) - .fill(0) - .map((_, sx) => createSection({ sx, sy }, config)) - ); - - return { config, sections }; + return { config, sections: [] }; } 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) { - const { sx, sy } = locateSection(change, board.config); - const section = board.sections[sy][sx]; + const sPos = locateSection(change, board.config); + const section = getSectionOnBoard(sPos, board); applyChange(change, section); } @@ -59,8 +80,8 @@ export function renderFullBoard(data: BoardData): FullBoard { continue; } - const { sx, sy } = locateSection({ x, y }, data.config); - const section = data.sections[sy][sx]; + const sPos = locateSection({ x, y }, data.config); + const section = getSectionOnBoard(sPos, data, { readOnly: true }); const xInSection = x % data.config.sectionWidth; const yInSection = y % data.config.sectionHeight; diff --git a/tests/board.test.ts b/tests/board.test.ts index 29cd7cd..ecedc47 100644 --- a/tests/board.test.ts +++ b/tests/board.test.ts @@ -3,7 +3,11 @@ import { assertEquals, } 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 { checkFullBoard } from "./checkFullBoard.ts"; import { locateSection } from "../logic/board.ts"; @@ -14,8 +18,8 @@ Deno.test("board", async (t) => { await t.step("createBoard", () => { board = createBoard({ - xSections: 2, - ySections: 2, + xSections: 3, + ySections: 3, sectionWidth: 4, sectionHeight: 3, defaultCh: " ", @@ -24,17 +28,8 @@ Deno.test("board", async (t) => { defaultWidth: 1, }); - assertEquals(board.sections.length, 2); - assertEquals(board.sections[0].length, 2); - 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); + // Sections are created on demand. + assertEquals(board.sections.length, 0); }); await t.step("locateSection", () => { @@ -73,10 +68,53 @@ Deno.test("board", async (t) => { 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", () => { assert(board); const rendered = renderFullBoard(board); 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); + }); }); From 91db478fe9d8a015359827f4d089b28ac9ceccfc Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Sun, 29 Dec 2024 22:32:59 +0800 Subject: [PATCH 2/9] fix: applying 1-width at odd location --- logic/section.ts | 2 +- tests/board.test.ts | 9 +++++++-- tests/section.test.ts | 23 ++++++++++++++--------- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/logic/section.ts b/logic/section.ts index d935dc3..f2463ff 100644 --- a/logic/section.ts +++ b/logic/section.ts @@ -44,7 +44,7 @@ export function applyChange(change: BoardChange, section: SectionData) { if (change.ch) { const chWidth = getCharacterWidth(change.ch); - const xCharacterOffset = xInSection % 2; + const xCharacterOffset = xInSection % chWidth; const offsetAdjustedXInSection = xInSection - xCharacterOffset; section.ch[yInSection][offsetAdjustedXInSection] = change.ch; section.width[yInSection][offsetAdjustedXInSection] = chWidth; diff --git a/tests/board.test.ts b/tests/board.test.ts index ecedc47..7e15725 100644 --- a/tests/board.test.ts +++ b/tests/board.test.ts @@ -51,11 +51,13 @@ Deno.test("board", async (t) => { applyChangeOnBoard({ x: 4, y: 0, ch: "B" }, board); applyChangeOnBoard({ x: 0, y: 3, ch: "C" }, board); applyChangeOnBoard({ x: 4, y: 3, ch: "D" }, board); + applyChangeOnBoard({ x: 5, y: 3, ch: "E" }, board); assertEquals(board.sections[0][0].ch[0][0], "A"); assertEquals(board.sections[0][1].ch[0][0], "B"); assertEquals(board.sections[1][0].ch[0][0], "C"); assertEquals(board.sections[1][1].ch[0][0], "D"); + assertEquals(board.sections[1][1].ch[0][1], "E"); applyChangeOnBoard({ x: 0, y: 1, ch: "你" }, board); applyChangeOnBoard({ x: 4, y: 2, ch: "好" }, board); @@ -65,7 +67,10 @@ Deno.test("board", async (t) => { assertEquals(board.sections[0][0].ch[1][0], "你"); assertEquals(board.sections[0][1].ch[2][0], "好"); assertEquals(board.sections[1][0].ch[1][0], "嗎"); - assertEquals(board.sections[1][1].ch[2][0], "嘛"); + assertEquals(board.sections[1][1].ch[2], ["嘛", "E", " ", " "]); + + applyChangeOnBoard({ x: 5, y: 4, ch: "啊" }, board); + assertEquals(board.sections[1][1].ch[2], ["啊", "E", " ", " "]); }); await t.step("getSectionOnBoard: existing section", () => { @@ -74,7 +79,7 @@ Deno.test("board", async (t) => { const section = getSectionOnBoard({ sx: 1, sy: 1 }, board, { readOnly: true, }); - assertEquals(section.ch[0][0], "嘛"); + assertEquals(section.ch[0][0], "啊"); assertEquals(section.color[0][0], "F"); assertEquals(section.bgColor[0][0], "0"); assertEquals(section.width[0][0], 2); diff --git a/tests/section.test.ts b/tests/section.test.ts index 8a5a139..07999f7 100644 --- a/tests/section.test.ts +++ b/tests/section.test.ts @@ -90,27 +90,32 @@ Deno.test("section", async (t) => { assert(section); applyChange({ x: 0, y: 0, ch: "t" }, section); - assertEquals(section.ch[0][0], "t"); - assertEquals(section.ch[0][1], " "); - assertEquals(section.width[0][0], 1); + assertEquals(section.ch[0], ["t", " ", " ", " "]); + assertEquals(section.width[0], [1, 1, 1, 1]); + }); + + await t.step("applyChange 1-width at odd position", () => { + assert(section); + + applyChange({ x: 1, y: 0, ch: "t" }, section); + assertEquals(section.ch[0], ["t", "t", " ", " "]); + assertEquals(section.width[0], [1, 1, 1, 1]); }); await t.step("applyChange 2-width at a correct position", () => { assert(section); applyChange({ x: 0, y: 0, ch: "あ" }, section); - assertEquals(section.ch[0][0], "あ"); - assertEquals(section.ch[0][1], " "); - assertEquals(section.width[0][0], 2); + assertEquals(section.ch[0], ["あ", "t", " ", " "]); + assertEquals(section.width[0], [2, 1, 1, 1]); }); await t.step("applyChange 2-width at an alternate position", () => { assert(section); applyChange({ x: 1, y: 0, ch: "あ" }, section); - assertEquals(section.ch[0][0], "あ"); - assertEquals(section.ch[0][1], " "); - assertEquals(section.width[0][0], 2); + assertEquals(section.ch[0], ["あ", "t", " ", " "]); + assertEquals(section.width[0], [2, 1, 1, 1]); }); await t.step("applyChange incorrect section", () => { From 587e0558e90dfaca571f1df2ba1759d84f041b02 Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Sun, 29 Dec 2024 22:43:36 +0800 Subject: [PATCH 3/9] fix: 0.3.1 --- deno.json | 2 +- logic/board.ts | 8 ++++---- tests/board.test.ts | 4 ++-- tests/checkFullBoard.ts | 5 +++++ 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/deno.json b/deno.json index 14c2394..95d1901 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@textplace/core", - "version": "0.3.0", + "version": "0.3.1", "exports": "./mod.ts", "imports": { "@deno/dnt": "jsr:@deno/dnt@^0.41.3" diff --git a/logic/board.ts b/logic/board.ts index 9dcb312..0436cdc 100644 --- a/logic/board.ts +++ b/logic/board.ts @@ -106,9 +106,9 @@ export function renderFullBoard(data: BoardData): FullBoard { return { w: lineLength, h: totalLineCount, - ch: ([] as string[]).concat(...chLines).flat(), - color: ([] as string[]).concat(...colorLines).flat(), - bg_color: ([] as string[]).concat(...bgColorLines).flat(), - width: ([] as number[]).concat(...widthLines).flat(), + ch: chLines.flat(), + color: colorLines.flat(), + bg_color: bgColorLines.flat(), + width: widthLines.flat(), }; } diff --git a/tests/board.test.ts b/tests/board.test.ts index 7e15725..886a2f4 100644 --- a/tests/board.test.ts +++ b/tests/board.test.ts @@ -119,7 +119,7 @@ Deno.test("board", async (t) => { await t.step("on-demand creation: only changed sections are saved", () => { assert(board); - assertEquals(board.sections.length, 2); - assertEquals(board.sections[0].length, 2); + assertEquals(board.sections[2], undefined); + assertEquals(board.sections[0][2], undefined); }); }); diff --git a/tests/checkFullBoard.ts b/tests/checkFullBoard.ts index 9984249..9484c40 100644 --- a/tests/checkFullBoard.ts +++ b/tests/checkFullBoard.ts @@ -47,6 +47,11 @@ export function checkFullBoard(board: FullBoard) { console.error("width: ", widthLine); }; + if (typeof cCh !== "string") { + printSituation(); + throw new Error("cCh is not string"); + } + if (!isValidColor(cCo) || !isValidColor(cBg)) { printSituation(); throw new Error("cCo or cBg is not valid"); From 46a647441bbc95eb97f737e90fb62874429699d7 Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Sun, 29 Dec 2024 23:13:00 +0800 Subject: [PATCH 4/9] fix: fill with reference --- logic/section.ts | 16 ++++++++-------- tests/board.test.ts | 11 +++++------ tests/section.test.ts | 1 + 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/logic/section.ts b/logic/section.ts index f2463ff..32256a6 100644 --- a/logic/section.ts +++ b/logic/section.ts @@ -16,17 +16,17 @@ export function createSection( const offsetX = sx * boardConfig.sectionWidth; const offsetY = sy * boardConfig.sectionHeight; - const ch: string[][] = Array(boardConfig.sectionHeight).fill( - Array(boardConfig.sectionWidth).fill(boardConfig.defaultCh), + const ch: string[][] = Array(boardConfig.sectionHeight).fill([]).map(() => + Array(boardConfig.sectionWidth).fill(boardConfig.defaultCh) ); - const color: string[][] = Array(boardConfig.sectionHeight).fill( - Array(boardConfig.sectionWidth).fill(boardConfig.defaultColor), + const color: string[][] = Array(boardConfig.sectionHeight).fill([]).map(() => + Array(boardConfig.sectionWidth).fill(boardConfig.defaultColor) ); - const bgColor: string[][] = Array(boardConfig.sectionHeight).fill( - Array(boardConfig.sectionWidth).fill(boardConfig.defaultBgColor), + const bgColor: string[][] = Array(boardConfig.sectionHeight).fill([]).map( + () => Array(boardConfig.sectionWidth).fill(boardConfig.defaultBgColor) ); - const width: number[][] = Array(boardConfig.sectionHeight).fill( - Array(boardConfig.sectionWidth).fill(boardConfig.defaultWidth), + const width: number[][] = Array(boardConfig.sectionHeight).fill([]).map(() => + Array(boardConfig.sectionWidth).fill(boardConfig.defaultWidth) ); return { offsetX, offsetY, ch, color, bgColor, width }; diff --git a/tests/board.test.ts b/tests/board.test.ts index 886a2f4..735bf32 100644 --- a/tests/board.test.ts +++ b/tests/board.test.ts @@ -56,8 +56,7 @@ Deno.test("board", async (t) => { assertEquals(board.sections[0][0].ch[0][0], "A"); assertEquals(board.sections[0][1].ch[0][0], "B"); assertEquals(board.sections[1][0].ch[0][0], "C"); - assertEquals(board.sections[1][1].ch[0][0], "D"); - assertEquals(board.sections[1][1].ch[0][1], "E"); + assertEquals(board.sections[1][1].ch[0], ["D", "E", " ", " "]); applyChangeOnBoard({ x: 0, y: 1, ch: "你" }, board); applyChangeOnBoard({ x: 4, y: 2, ch: "好" }, board); @@ -67,10 +66,10 @@ Deno.test("board", async (t) => { assertEquals(board.sections[0][0].ch[1][0], "你"); assertEquals(board.sections[0][1].ch[2][0], "好"); assertEquals(board.sections[1][0].ch[1][0], "嗎"); - assertEquals(board.sections[1][1].ch[2], ["嘛", "E", " ", " "]); + assertEquals(board.sections[1][1].ch[1], ["嘛", " ", " ", " "]); applyChangeOnBoard({ x: 5, y: 4, ch: "啊" }, board); - assertEquals(board.sections[1][1].ch[2], ["啊", "E", " ", " "]); + assertEquals(board.sections[1][1].ch[1], ["啊", " ", " ", " "]); }); await t.step("getSectionOnBoard: existing section", () => { @@ -79,10 +78,10 @@ Deno.test("board", async (t) => { const section = getSectionOnBoard({ sx: 1, sy: 1 }, board, { readOnly: true, }); - assertEquals(section.ch[0][0], "啊"); + assertEquals(section.ch[0], ["D", "E", " ", " "]); assertEquals(section.color[0][0], "F"); assertEquals(section.bgColor[0][0], "0"); - assertEquals(section.width[0][0], 2); + assertEquals(section.width[0], [1, 1, 1, 1]); }); await t.step("getSectionOnBoard: non-existing row", () => { diff --git a/tests/section.test.ts b/tests/section.test.ts index 07999f7..a70da5b 100644 --- a/tests/section.test.ts +++ b/tests/section.test.ts @@ -91,6 +91,7 @@ Deno.test("section", async (t) => { applyChange({ x: 0, y: 0, ch: "t" }, section); assertEquals(section.ch[0], ["t", " ", " ", " "]); + assertEquals(section.ch[1], [" ", " ", " ", " "]); assertEquals(section.width[0], [1, 1, 1, 1]); }); From 645b7202222cf277f19c83c486cb5ec87d63c2f7 Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Sun, 29 Dec 2024 23:13:14 +0800 Subject: [PATCH 5/9] chore: add test watch mode to zed command --- .zed/tasks.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.zed/tasks.json b/.zed/tasks.json index 4868825..34420e6 100644 --- a/.zed/tasks.json +++ b/.zed/tasks.json @@ -1,9 +1,10 @@ -// Static tasks configuration. -// -// Example: [ { "label": "Test", "command": "deno test" + }, + { + "label": "Test - Watch", + "command": "deno test --watch" } ] From 97ad480ad8dc24cc5e33a1a041d3a2d161cadcfd Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Sun, 29 Dec 2024 23:13:27 +0800 Subject: [PATCH 6/9] 0.3.2 --- deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno.json b/deno.json index 95d1901..948c947 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@textplace/core", - "version": "0.3.1", + "version": "0.3.2", "exports": "./mod.ts", "imports": { "@deno/dnt": "jsr:@deno/dnt@^0.41.3" From 8940f26f179e68759de56b05abbcad505972793e Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Sun, 29 Dec 2024 23:14:43 +0800 Subject: [PATCH 7/9] fix: format --- logic/section.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/section.ts b/logic/section.ts index 32256a6..c3f976c 100644 --- a/logic/section.ts +++ b/logic/section.ts @@ -23,7 +23,7 @@ export function createSection( Array(boardConfig.sectionWidth).fill(boardConfig.defaultColor) ); const bgColor: string[][] = Array(boardConfig.sectionHeight).fill([]).map( - () => Array(boardConfig.sectionWidth).fill(boardConfig.defaultBgColor) + () => Array(boardConfig.sectionWidth).fill(boardConfig.defaultBgColor), ); const width: number[][] = Array(boardConfig.sectionHeight).fill([]).map(() => Array(boardConfig.sectionWidth).fill(boardConfig.defaultWidth) From 4dd8121ebb60c023465199d565557699cf70fea6 Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Tue, 28 Jan 2025 14:31:14 +0800 Subject: [PATCH 8/9] feat: support any unicode grapheme clusters. --- deno.json | 5 +++-- deno.lock | 7 ++++++- logic/character.ts | 13 +++---------- tests/character.test.ts | 6 ++---- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/deno.json b/deno.json index 948c947..2ac2e2b 100644 --- a/deno.json +++ b/deno.json @@ -1,8 +1,9 @@ { "name": "@textplace/core", - "version": "0.3.2", + "version": "0.4.0", "exports": "./mod.ts", "imports": { - "@deno/dnt": "jsr:@deno/dnt@^0.41.3" + "@deno/dnt": "jsr:@deno/dnt@^0.41.3", + "@std/cli": "jsr:@std/cli@^1.0.11" } } diff --git a/deno.lock b/deno.lock index 491b9f0..afae4c4 100644 --- a/deno.lock +++ b/deno.lock @@ -7,6 +7,7 @@ "jsr:@std/assert@0.223": "0.223.0", "jsr:@std/assert@0.226": "0.226.0", "jsr:@std/bytes@0.223": "0.223.0", + "jsr:@std/cli@^1.0.11": "1.0.11", "jsr:@std/fmt@0.223": "0.223.0", "jsr:@std/fmt@1": "1.0.3", "jsr:@std/fs@0.223": "0.223.0", @@ -54,6 +55,9 @@ "@std/bytes@0.223.0": { "integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8" }, + "@std/cli@1.0.11": { + "integrity": "ec219619fdcd31bcf0d8e53bee1e2706ec9a02f70255365a094f69755dadd340" + }, "@std/fmt@0.223.0": { "integrity": "6deb37794127dfc7d7bded2586b9fc6f5d50e62a8134846608baf71ffc1a5208" }, @@ -151,7 +155,8 @@ }, "workspace": { "dependencies": [ - "jsr:@deno/dnt@~0.41.3" + "jsr:@deno/dnt@~0.41.3", + "jsr:@std/cli@^1.0.11" ] } } diff --git a/logic/character.ts b/logic/character.ts index d1b9f69..074001d 100644 --- a/logic/character.ts +++ b/logic/character.ts @@ -1,7 +1,6 @@ +import { unicodeWidth } from "@std/cli/unicode-width"; + const segmenter = new Intl.Segmenter("en", { granularity: "grapheme" }); -const cjkRegex = - /[\p{Unified_Ideograph}\u30A0-\u30FF\u3040-\u309F\u31F0-\u31FF]/u; -const printableASCIIRegex = /^[\x20-\x7E]$/; export function getCharacterWidth(ch: string): number { const segments = [...segmenter.segment(ch)]; @@ -11,11 +10,5 @@ export function getCharacterWidth(ch: string): number { ); } - const matchesASCII = ch.match(printableASCIIRegex); - const matchesCJK = ch.match(cjkRegex); - - if (!matchesASCII && !matchesCJK) throw new Error(`Invalid character: ${ch}`); - - // TODO: Support Emojis. - return matchesCJK ? 2 : 1; + return unicodeWidth(ch); } diff --git a/tests/character.test.ts b/tests/character.test.ts index 886d7ad..d1330f9 100644 --- a/tests/character.test.ts +++ b/tests/character.test.ts @@ -26,14 +26,12 @@ Deno.test("getCharacterWidth CJK", () => { assertEquals(getCharacterWidth("グ"), 2); assertEquals(getCharacterWidth("ソ"), 2); - assertThrows(() => getCharacterWidth("?")); - assertThrows(() => getCharacterWidth("!")); + assertEquals(getCharacterWidth("?"), 2); + assertEquals(getCharacterWidth("!"), 2); assertThrows(() => getCharacterWidth("你好")); assertThrows(() => getCharacterWidth("ヨスガノ")); }); Deno.test("getCharacterWidth previously faulty cases", () => { assertEquals(getCharacterWidth("𤲶"), 2); - - assertThrows(() => getCharacterWidth("𤲶"[0])); }); From 7923680e80d395ab1bdae7019348cdf932dd69ce Mon Sep 17 00:00:00 2001 From: Shibo Lyu Date: Tue, 28 Jan 2025 17:21:59 +0800 Subject: [PATCH 9/9] fix: dirty fix for char width --- deno.json | 2 +- logic/character.ts | 3 ++- tests/character.test.ts | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/deno.json b/deno.json index 2ac2e2b..ffdf2b3 100644 --- a/deno.json +++ b/deno.json @@ -1,6 +1,6 @@ { "name": "@textplace/core", - "version": "0.4.0", + "version": "0.4.1", "exports": "./mod.ts", "imports": { "@deno/dnt": "jsr:@deno/dnt@^0.41.3", diff --git a/logic/character.ts b/logic/character.ts index 074001d..2d58dac 100644 --- a/logic/character.ts +++ b/logic/character.ts @@ -10,5 +10,6 @@ export function getCharacterWidth(ch: string): number { ); } - return unicodeWidth(ch); + // TODO: Properly fix this. + return Math.min(unicodeWidth(ch), 2); } diff --git a/tests/character.test.ts b/tests/character.test.ts index d1330f9..c8e3a65 100644 --- a/tests/character.test.ts +++ b/tests/character.test.ts @@ -32,6 +32,12 @@ Deno.test("getCharacterWidth CJK", () => { assertThrows(() => getCharacterWidth("ヨスガノ")); }); +Deno.test("getCharacterWidth Emoji", () => { + assertEquals(getCharacterWidth("👋"), 2); + assertEquals(getCharacterWidth("🌲️"), 2); + assertEquals(getCharacterWidth("👨‍👩‍👧‍👦"), 2); +}); + Deno.test("getCharacterWidth previously faulty cases", () => { assertEquals(getCharacterWidth("𤲶"), 2); });