From b32fe228d2d6058862c465c2237b71dd9710a6bf Mon Sep 17 00:00:00 2001 From: stjet <49297268+stjet@users.noreply.github.com> Date: Thu, 8 Feb 2024 22:44:47 +0000 Subject: [PATCH] add strikethroughs --- .gitignore | 2 - README.md | 1 + index.ts | 2 + makoto.ts | 109 +++++++++++++++++++++++++++++++++++---------- tsconfig-node.json | 18 ++++++++ tsconfig-web.json | 22 +++++++++ 6 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 tsconfig-node.json create mode 100644 tsconfig-web.json diff --git a/.gitignore b/.gitignore index 87ffa6a..a705410 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ *.js tsconfig.json -tsconfig-node.json -tsconfig-web.json \ No newline at end of file diff --git a/README.md b/README.md index 713922b..6e07904 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ Some example CSS to style the HTML output can be found in `styles/makoto.css`. - `italic-not-closed` - `bold-not-closed` - `superscript-not-closed` +- `strikethrough-not-closed` - `blockquote-broken` - `code-block-not-closed` - `unordered-list-broken` diff --git a/index.ts b/index.ts index d2f1147..45bd397 100644 --- a/index.ts +++ b/index.ts @@ -104,6 +104,8 @@ test_assert_equal(parse_md_to_html("1. uno\n2. dos\n3. tres\n4. cuatro\n5. cinco test_assert_equal(parse_md_to_html("a ^ace^ base\n```js\n^sup^\n```\ne=mc^2\n^ea^"), "

a ace base

\n
\n^sup^
\n
\n

e=mc^2

\n

ea

", "superscript test"); +test_assert_equal(parse_md_to_html("~~asdf~~ testing ~~is this thing on?~~ this will not be ~~struck through\n~~wee\n~~**a a a**\n~~*aloha~~*"), "

asdf testing is this thing on? this will not be ~~struck through

\n

~~wee

\n

~~a a a

\n

*aloha*

", "strikethrough test"); + //I don't care about table edgecases. test_assert_equal(parse_md_to_html("|a|b|c|\n|d|e|f|\n# a"), "\n\n\n\n\n\n\n\n\n\n\n
abc
def
\n

a

", "basic table test"); diff --git a/makoto.ts b/makoto.ts index 744f2f4..0832c14 100644 --- a/makoto.ts +++ b/makoto.ts @@ -5,6 +5,22 @@ export type ParseResult = { warnings: Warning[] } +function replace_last_element(html_line: string, html_element: string, replace_string: string): string { + let split: string[] = html_line.split(html_element); + html_line = ""; + for (let ii=0; ii < split.length; ii++) { + html_line += split[ii]; + if (ii !== split.length-1) { + if (ii === split.length-2) { + html_line += replace_string; + } else { + html_line += html_element; + } + } + } + return html_line; +} + //some minor differences with markdown spec? export function parse_md_to_html_with_warnings(md: string): ParseResult { let html: string = ""; @@ -22,6 +38,8 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult { let asterisk_num: number = 0; let asterisk_out_num: number = 0; let in_asterisk: boolean = false; + let tilde_num: number = 0; + let in_strikethrough: boolean = false; let horizontal_num: number = 0; let horizontal_rule: boolean = false; let was_image: boolean = false; @@ -185,6 +203,16 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult { if (chars[i-1] === "\n") { html_line = "

"; } + //ending strikethrough + if (in_strikethrough && char === "~" && tilde_num === 1) { + in_strikethrough = false; + html_line += ""; + tilde_num = 0; + add_char = false; + // + } else if (tilde_num === 1) { + html_line += "~"; + } //ending a bold/italic? if (in_asterisk && char === "*") { if (asterisk_num === 2 && chars[i-1] === "*") { @@ -209,22 +237,22 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult { html_line += char; } } + if (in_strikethrough) { + //strikethrough never ended + //remove the last and replace it with a ~~ + in_strikethrough = false; + html_line = replace_last_element(html_line, "", "~~"); + warnings.push({ + type: "strikethrough-not-closed", + message: "Strikethrough not closed, may be missing closing '~~'? Backslash the '~'s if this is intentional", + line_number, + }); + } if (in_asterisk) { //bold/italic never ended if (asterisk_num === 1) { //remove the last and replace it with a * - let split: string[] = html_line.split(""); - html_line = ""; - for (let ii=0; ii < split.length; ii++) { - html_line += split[ii]; - if (ii !== split.length-1) { - if (ii === split.length-2) { - html_line += "*"; - } else { - html_line += ""; - } - } - } + html_line = replace_last_element(html_line, "", "*"); warnings.push({ type: "italic-not-closed", message: "Italic not closed, may be missing closing '*'? Backslash the '*' if this is intentional", @@ -232,18 +260,7 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult { }); } else if (asterisk_num === 2) { //remove the last and replace it with a ** - let split: string[] = html_line.split(""); - html_line = ""; - for (let ii=0; ii < split.length; ii++) { - html_line += split[ii]; - if (ii !== split.length-1) { - if (ii === split.length-2) { - html_line += "**"; - } else { - html_line += ""; - } - } - } + html_line = replace_last_element(html_line, "", "**"); warnings.push({ type: "bold-not-closed", message: "Bold not closed, may be missing closing '**'? Backslash the '**' if this is intentional", @@ -707,6 +724,50 @@ export function parse_md_to_html_with_warnings(md: string): ParseResult { if (i === 0 || chars[i-1] === "\n") { html_line = "

"+html_line; } + //handle strikethrough + if (char === "~") { + tilde_num++; + if (tilde_num === 2) { + if (in_strikethrough) { + //end strikethrough + html_line += ""; + in_strikethrough = false; + //end italics and bolds if not ended + if (in_asterisk) { + //bold/italic never ended + if (asterisk_num === 1) { + //remove the last and replace it with a * + html_line = replace_last_element(html_line, "", "*"); + warnings.push({ + type: "italic-not-closed", + message: "Italic not closed, may be missing closing '*'? Backslash the '*' if this is intentional", + line_number, + }); + } else if (asterisk_num === 2) { + //remove the last and replace it with a ** + html_line = replace_last_element(html_line, "", "**"); + warnings.push({ + type: "bold-not-closed", + message: "Bold not closed, may be missing closing '**'? Backslash the '**' if this is intentional", + line_number, + }); + } + asterisk_num = 0; + asterisk_out_num = 0; + in_asterisk = false; + } + } else { + //start strikethrough + html_line += ""; + in_strikethrough = true; + } + tilde_num = 0; + } + continue; + } else if (tilde_num > 0) { + html_line += "~".repeat(tilde_num); + tilde_num = 0; + } //handle italics and bolds if (char === "*" && asterisk_num < 2 && !in_asterisk) { asterisk_num++; diff --git a/tsconfig-node.json b/tsconfig-node.json new file mode 100644 index 0000000..6bcfab2 --- /dev/null +++ b/tsconfig-node.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "strict": true, + "moduleResolution": "node", + "typeRoots": [ + "./node_modules/@types", + ], + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "exclude": [ + "node_modules", + ".build" + ] +} \ No newline at end of file diff --git a/tsconfig-web.json b/tsconfig-web.json new file mode 100644 index 0000000..1453c7c --- /dev/null +++ b/tsconfig-web.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "strict": true, + "moduleResolution": "classic", + "typeRoots": [ + "./node_modules/@types", + ], + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "lib": [ + "ESNext", + "DOM" + ], + "exclude": [ + "node_modules", + ".build" + ] +} \ No newline at end of file