]*)?>([^<]*)<\/code>/g;
const ENTITY = /&(?:#\d+|#x[0-9a-f]+|[a-z]+);/gi;
-export const markRawCode = (html) => {
+export const markRawCode = (html, room = { left: Infinity }) => {
if (html.includes(' {
- if (/\bclass=/.test(attributes) || !isShort(text.replace(ENTITY, '_'), false)) return whole;
+ const plain = text.replace(ENTITY, '_');
+ if (breaksAtSeparators(plain, false)) {
+ return spend(room, breakCount(text)) ? `${withBreaks(text)}` : whole;
+ }
+ if (/\bclass=/.test(attributes) || !isShort(plain, false)) return whole;
return `${text}`;
});
};
+// A code element whose children are all text gets them back split, a `wbr`
+// element after each separator. One already split is left alone.
+const breakAtSeparators = (ctx, code, room) => {
+ const children = code.children ?? [];
+ if (!children.length || children.some((child) => child.type !== 'text')) return;
+ if (!spend(room, breakCount(children.map((child) => child.value).join('')))) return;
+ // split() with a capturing group puts each separator at an odd index.
+ const parts = children.map((child) => child.value).join('').split(SEPARATORS).flatMap((piece, index) => [
+ ...(piece ? [{ type: 'text', value: piece }] : []),
+ ...(index % 2 ? [{ type: 'element', tagName: 'wbr', properties: {}, children: [] }] : []),
+ ]);
+ ctx.replaceNode(code, { type: 'element', tagName: 'code', properties: { ...(code.properties ?? {}) }, children: parts });
+};
+
+// The page's budget of breaks, kept with the page being built.
+const breakRoom = (ctx) => {
+ ctx.data.rbBreaks ??= { left: BREAK_BUDGET };
+ return ctx.data.rbBreaks;
+};
+
export default function shortCode() {
return {
name: 'rb-short-code',
raw(node, ctx) {
+ const room = breakRoom(ctx);
// Inline HTML in Markdown (a hash in a table cell) arrives in three
// nodes: the opening tag, the text, the closing tag.
const open = /^]*)?>$/.exec(node.value ?? '');
@@ -65,13 +119,17 @@ export default function shortCode() {
const siblings = ctx.parent(node)?.children ?? [];
const index = ctx.indexOf(node) ?? -1;
const [text, close] = [siblings[index + 1], siblings[index + 2]];
- if (text?.type === 'text' && close?.type === 'raw' && close.value === ''
- && !/\bclass=/.test(open[1] ?? '') && isShort(text.value, false)) {
+ if (text?.type !== 'text' || close?.type !== 'raw' || close.value !== '') return;
+ if (breaksAtSeparators(text.value, false)) {
+ if (spend(room, breakCount(text.value))) {
+ ctx.replaceNode(text, { type: 'raw', value: withBreaks(escapeText(text.value)) });
+ }
+ } else if (!/\bclass=/.test(open[1] ?? '') && isShort(text.value, false)) {
ctx.replaceNode(node, { type: 'raw', value: `