diff --git a/scripts/issue-body.js b/scripts/issue-body.js index 972a282..bbff973 100644 --- a/scripts/issue-body.js +++ b/scripts/issue-body.js @@ -146,6 +146,12 @@ function isSeparator(cells) { * 在指定段落裡就地更新(或補上)一行「前綴+值」。 * * 用於人天估算這種「議題上只該有一行、重跑要覆蓋而不是累積」的欄位。 + * + * 三件事都限定在目標段落之內,因為弄錯的代價是靜靜改壞別人的內容: + * - 標題要完全相同才算數,`## 關聯度說明` 不是 `## 關聯`。 + * - 圍欄裡的假標題不算標題,沿用本檔共同的圍欄判斷。 + * - 找既有那一行只在段落範圍內找,別的段落剛好有同前綴時不會被改掉。 + * * 段落不存在時補在 body 結尾——寧可放錯位置,也不要讓值靜靜消失。 * * @param {string} body 議題 body @@ -155,26 +161,42 @@ function isSeparator(cells) { */ export function upsertLineInSection(body, section, line) { const prefix = line.slice(0, line.indexOf(':') + 1); - const existing = new RegExp(`^${escapeRegExp(prefix)}.*$`, 'm'); + const rows = [...eachLine(body)]; + const { start, end } = sectionBounds(rows, section); - if (existing.test(body)) { - const updated = body.replace(existing, line); - return updated; - } - - const heading = `## ${section}`; - const start = body.indexOf(heading); if (start === -1) { return `${body.replace(/\n*$/, '')}\n\n${line}\n`; } - // 段落的結尾是下一個標題之前,或整份 body 的結尾 - const after = body.indexOf('\n## ', start + heading.length); - const end = after === -1 ? body.length : after; - const head = body.slice(0, end).replace(/\n*$/, ''); - return `${head}\n${line}\n${body.slice(end).replace(/^\n+/, '\n')}`; + const text = rows.map((row) => row.line); + for (let i = start + 1; i < end; i += 1) { + if (rows[i].inFence || !text[i].startsWith(prefix)) continue; + if (text[i] === line) return body; + text[i] = line; + return text.join('\n'); + } + + // 插在段落內容的結尾,跳過段落與段落之間的空行 + let insertAt = end; + while (insertAt > start + 1 && text[insertAt - 1].trim() === '') insertAt -= 1; + text.splice(insertAt, 0, line); + return text.join('\n'); } -function escapeRegExp(text) { - return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +/** 找出段落的起訖行號;start 為標題那一行,end 為下一個標題(或結尾) */ +function sectionBounds(rows, section) { + let start = -1; + + for (let i = 0; i < rows.length; i += 1) { + if (rows[i].inFence) continue; + const heading = rows[i].line.match(/^##\s+(.+?)\s*$/); + if (!heading) continue; + + if (start === -1) { + if (heading[1] === section) start = i; + continue; + } + return { start, end: i }; + } + return { start, end: rows.length }; }