fix(markdown): require double-tilde strikethrough delimiters
This commit is contained in:
@@ -1448,9 +1448,23 @@
|
||||
}
|
||||
|
||||
// Configure marked with syntax highlighting and HTML escaping for text
|
||||
const strictStrikethroughRegex = /^(~~)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/;
|
||||
|
||||
marked.use({
|
||||
breaks: true,
|
||||
gfm: true,
|
||||
tokenizer: {
|
||||
del(src) {
|
||||
const match = strictStrikethroughRegex.exec(src);
|
||||
if (!match) return undefined;
|
||||
return {
|
||||
type: 'del',
|
||||
raw: match[0],
|
||||
text: match[2],
|
||||
tokens: this.lexer.inlineTokens(match[2])
|
||||
};
|
||||
}
|
||||
},
|
||||
renderer: {
|
||||
// Code blocks: syntax highlight, no HTML escaping
|
||||
code(token) {
|
||||
|
||||
@@ -1,8 +1,32 @@
|
||||
import { marked, type Token } from "marked";
|
||||
import { Marked, type Token, Tokenizer, type Tokens } from "marked";
|
||||
import { isImageLine } from "../terminal-image.js";
|
||||
import type { Component } from "../tui.js";
|
||||
import { applyBackgroundToLine, visibleWidth, wrapTextWithAnsi } from "../utils.js";
|
||||
|
||||
const STRICT_STRIKETHROUGH_REGEX = /^(~~)(?=[^\s~])((?:\\.|[^\\])*?(?:\\.|[^\s~\\]))\1(?=[^~]|$)/;
|
||||
|
||||
class StrictStrikethroughTokenizer extends Tokenizer {
|
||||
override del(src: string): Tokens.Del | undefined {
|
||||
const match = STRICT_STRIKETHROUGH_REGEX.exec(src);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const text = match[2];
|
||||
return {
|
||||
type: "del",
|
||||
raw: match[0],
|
||||
text,
|
||||
tokens: this.lexer.inlineTokens(text),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const markdownParser = new Marked();
|
||||
markdownParser.setOptions({
|
||||
tokenizer: new StrictStrikethroughTokenizer(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Default text styling for markdown content.
|
||||
* Applied to all text unless overridden by markdown formatting.
|
||||
@@ -112,7 +136,7 @@ export class Markdown implements Component {
|
||||
const normalizedText = this.text.replace(/\t/g, " ");
|
||||
|
||||
// Parse markdown to HTML-like tokens
|
||||
const tokens = marked.lexer(normalizedText);
|
||||
const tokens = markdownParser.lexer(normalizedText);
|
||||
|
||||
// Convert tokens to styled terminal output
|
||||
const renderedLines: string[] = [];
|
||||
|
||||
@@ -1061,6 +1061,31 @@ bar`,
|
||||
});
|
||||
});
|
||||
|
||||
describe("Strikethrough syntax", () => {
|
||||
it("should render ~~text~~ as strikethrough", () => {
|
||||
const markdown = new Markdown("Use ~~strikethrough~~ here", 0, 0, defaultMarkdownTheme);
|
||||
|
||||
const lines = markdown.render(80);
|
||||
const joinedOutput = lines.join("\n");
|
||||
const joinedPlain = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")).join(" ");
|
||||
|
||||
assert.ok(joinedOutput.includes("\x1b[9m"), "Should apply strikethrough styling");
|
||||
assert.ok(joinedPlain.includes("strikethrough"), "Should include struck text content");
|
||||
assert.ok(!joinedPlain.includes("~~strikethrough~~"), "Should not render delimiters as text");
|
||||
});
|
||||
|
||||
it("should keep ~text~ as plain text", () => {
|
||||
const markdown = new Markdown("Use ~strikethrough~ literally", 0, 0, defaultMarkdownTheme);
|
||||
|
||||
const lines = markdown.render(80);
|
||||
const joinedOutput = lines.join("\n");
|
||||
const joinedPlain = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")).join(" ");
|
||||
|
||||
assert.ok(joinedPlain.includes("~strikethrough~"), "Single-tilde delimiters should remain visible");
|
||||
assert.ok(!joinedOutput.includes("\x1b[9m"), "Single-tilde text should not use strikethrough styling");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Links", () => {
|
||||
it("should not duplicate URL for autolinked emails", () => {
|
||||
const markdown = new Markdown("Contact user@example.com for help", 0, 0, defaultMarkdownTheme);
|
||||
|
||||
Reference in New Issue
Block a user