From 1eb6d179be81553027d31c0bfeb2f989694605c6 Mon Sep 17 00:00:00 2001 From: Tangent Wantwight Date: Sat, 26 Aug 2023 01:16:22 -0400 Subject: [PATCH] Add helpers for working with words --- src/words.test.ts | 60 +++++++++++++++++++++++++++++++++++++++++++++++ src/words.ts | 44 ++++++++++++++++++++++++++++++---- 2 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 src/words.test.ts diff --git a/src/words.test.ts b/src/words.test.ts new file mode 100644 index 0000000..f55bdb8 --- /dev/null +++ b/src/words.test.ts @@ -0,0 +1,60 @@ +import { AsHtml, AsText, Concat } from "./words"; + +describe("Text Words", () => { + test.each([ + [{ enchanted: "apple" }, "apple"], + [{ enchanted: "" }, ""], + [{ text: "banana" }, "banana"], + [{ text: "kiwi" }, "kiwi"], + [{ html: "cherry" }, "cherry"], + ])("AsText(%s)", (word, expected) => expect(AsText(word)).toEqual(expected)); + + test.each([ + [{ enchanted: "apple" }, "apple"], + [{ enchanted: "" }, "<pie>"], + [{ text: "banana" }, "banana"], + [{ text: "kiwi" }, "<b>kiwi"], + [{ html: "cherry" }, "cherry"], + ])("AsHtml(%s)", (word, expected) => expect(AsHtml(word)).toEqual(expected)); + + test.each([ + [null, { enchanted: "1" }, { text: "1" }], + [null, { enchanted: ">1" }, { text: ">1" }], + [null, { text: "2" }, { text: "2" }], + [null, { text: "3" }, { text: "3" }], + [null, { html: "2" }, { html: "2" }], + [null, { html: "3" }, { html: "3" }], + [{ enchanted: "&pple" }, { enchanted: "1" }, { text: "&pple1" }], + [{ enchanted: "&pple" }, { enchanted: ">1" }, { text: "&pple>1" }], + [{ enchanted: "&pple" }, { text: "2" }, { text: "&pple2" }], + [{ enchanted: "&pple" }, { text: "3" }, { text: "&pple3" }], + [{ enchanted: "&pple" }, { html: "2" }, { html: "&pple2" }], + [ + { enchanted: "&pple" }, + { html: "3" }, + { html: "&pple3" }, + ], + [{ text: "anana" }, { enchanted: "1" }, { text: "anana1" }], + [{ text: "anana" }, { enchanted: ">1" }, { text: "anana>1" }], + [{ text: "anana" }, { text: "2" }, { text: "anana2" }], + [{ text: "anana" }, { text: "3" }, { text: "anana3" }], + [{ text: "anana" }, { html: "2" }, { html: "<b>anana2" }], + [ + { text: "anana" }, + { html: "3" }, + { html: "<b>anana3" }, + ], + [{ html: "" }, { enchanted: "1" }, { html: "1" }], + [{ html: "" }, { enchanted: ">1" }, { html: ">1" }], + [{ html: "" }, { text: "2" }, { html: "2" }], + [ + { html: "" }, + { text: "3" }, + { html: "<b>3</b>" }, + ], + [{ html: "" }, { html: "2" }, { html: "2" }], + [{ html: "" }, { html: "3" }, { html: "3" }], + ])("Concat(%s, %s)", (left, right, expected) => + expect(Concat(left, right)).toEqual(expected) + ); +}); diff --git a/src/words.ts b/src/words.ts index 151a2fb..31e26e8 100644 --- a/src/words.ts +++ b/src/words.ts @@ -1,3 +1,5 @@ +import { escapeHtml } from "./helpers"; + /** * A word whose value is text with provenance- this literal value appeared in the source * code, and was not the result of any backslash, variable, or command substitutions. @@ -29,7 +31,7 @@ export type HtmlWord = { html: string; }; -export type TextPiece = TextWord; +export type TextPiece = EnchantedWord | TextWord | HtmlWord; export type VariablePiece = { variable: string }; export type ScriptPiece = { script: Script }; export type InterpolatedPiece = TextPiece | VariablePiece | ScriptPiece; @@ -42,18 +44,50 @@ export type InterpolatedWord = { pieces: InterpolatedPiece[]; }; +export function AsText(word: TextPiece): string { + if ("enchanted" in word) { + return word.enchanted; + } else if ("text" in word) { + return word.text; + } else if ("html" in word) { + return word.html; + } else { + return ""; + } +} +export function AsHtml(word: TextPiece): string { + if ("enchanted" in word) { + return escapeHtml(word.enchanted); + } else if ("text" in word) { + return escapeHtml(word.text); + } else if ("html" in word) { + return word.html; + } else { + return ""; + } +} + // safely concatenate text pieces, converting as needed -export function Concat(left: TextPiece, right: TextPiece) { - return { text: left.text + right.text }; +export function Concat(left: TextPiece | null, right: TextPiece) { + if (left === null) { + return "enchanted" in right ? { text: right.enchanted } : right; + } + if ("html" in left || "html" in right) { + return { html: AsHtml(left) + AsHtml(right) }; + } else { + return { text: AsText(left) + AsText(right) }; + } } function IsTextPiece(piece: InterpolatedPiece | undefined): piece is TextPiece { - return piece ? "text" in piece : false; + return piece + ? "text" in piece || "enchanted" in piece || "html" in piece + : false; } export function SimplifyWord( pieces: InterpolatedPiece[] -): InterpolatedWord | TextWord { +): InterpolatedWord | EnchantedWord | TextWord | HtmlWord { const consolidated: InterpolatedPiece[] = []; for (const piece of pieces) { const top = consolidated[consolidated.length - 1];