diff --git a/extensions/discord/src/chunk.test.ts b/extensions/discord/src/chunk.test.ts index 11b28749d24b..0e9c81d15434 100644 --- a/extensions/discord/src/chunk.test.ts +++ b/extensions/discord/src/chunk.test.ts @@ -78,6 +78,17 @@ describe("chunkDiscordText", () => { } }); + it("keeps chunks within maxChars when a closing fence line carries trailing text", () => { + // A line that both closes the fence and carries a long tail must still reserve closing-fence + // space; otherwise a mid-line flush appended "```" and overflowed maxChars (e.g. 2004 > 2000). + for (let pad = 1990; pad <= 2000; pad++) { + const text = "hi\n```lang\n```" + "z".repeat(pad); + for (const chunk of chunkDiscordText(text, { maxChars: 2000, maxLines: 100 })) { + expect(chunk.length).toBeLessThanOrEqual(2000); + } + } + }); + it("preserves whitespace when splitting long lines", () => { const text = Array.from({ length: 40 }, () => "word").join(" "); const chunks = chunkDiscordText(text, { maxChars: 20, maxLines: 50 }); diff --git a/extensions/discord/src/chunk.ts b/extensions/discord/src/chunk.ts index d78050605218..952dc150c6d3 100644 --- a/extensions/discord/src/chunk.ts +++ b/extensions/discord/src/chunk.ts @@ -207,8 +207,12 @@ export function chunkDiscordText(text: string, opts: ChunkDiscordTextOpts = {}): } } - const reserveChars = nextOpenFence ? closeFenceLine(nextOpenFence).length + 1 : 0; - const reserveLines = nextOpenFence ? 1 : 0; + // A flush can fire mid-line, before `openFence` advances to `nextOpenFence` below, so it closes + // against the still-open `openFence`. A fence-closing line that also carries trailing text would + // otherwise reserve 0 yet still get a closing fence appended on flush, overflowing maxChars. + const fenceToReserve = nextOpenFence ?? openFence; + const reserveChars = fenceToReserve ? closeFenceLine(fenceToReserve).length + 1 : 0; + const reserveLines = fenceToReserve ? 1 : 0; const effectiveMaxChars = maxChars - reserveChars; const effectiveMaxLines = maxLines - reserveLines; const charLimit = effectiveMaxChars > 0 ? effectiveMaxChars : maxChars;