























@@ -58,7 +58,7 @@ const allProofFieldNames = requiredProofFields
5858.concat(["Before evidence", "Before evidence optional"]);
59596060const missingValueRegex =
61-/^(?:n\/?a|not applicable|tbd|todo|unknown|unsure|none provided|no evidence|not tested|untested|-|\[[^\]]*\])$/i;
61+/^(?:n\/?a|not applicable|tbd|todo|unknown|unsure|none provided|no evidence|not tested|untested|did not test|didn't test|could not test|couldn't test|-|\[[^\]]*\])\.?$/i;
62626363const standaloneMissingProofRegex =
6464/^\s*(?:[-*]\s*)?(?:n\/?a|not applicable|not tested|untested|no evidence|did not test|didn't test|could not test|couldn't test)\s*\.?\s*$/im;
@@ -208,20 +208,51 @@ export async function isMaintainerTeamMember({
208208return body?.state === "active";
209209}
210210211+function nextFenceMarker(line, fenceMarker) {
212+const fence = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
213+const marker = fence?.[1] ?? "";
214+const suffix = fence?.[2] ?? "";
215+if (!fenceMarker && marker) {
216+return marker;
217+}
218+if (
219+fenceMarker &&
220+marker[0] === fenceMarker[0] &&
221+marker.length >= fenceMarker.length &&
222+suffix.trim() === ""
223+) {
224+return "";
225+}
226+return fenceMarker;
227+}
228+229+function isMarkdownHeadingLine(line) {
230+return /^#{1,6}\s+\S/.test(line);
231+}
232+211233function extractMarkdownSections(headingRegex, body = "") {
212234// Normalize CRLF → LF so regexes and section slicing see GitHub web-editor PR
213235// bodies the same way as locally-authored Markdown.
214236const normalizedBody = normalizeLineEndings(body);
215237const sections = [];
216-const matcher = new RegExp(
217-headingRegex.source,
218-headingRegex.flags.includes("g") ? headingRegex.flags : `${headingRegex.flags}g`,
219-);
220-for (const match of normalizedBody.matchAll(matcher)) {
221-const sectionStart = match.index + match[0].length;
222-const rest = normalizedBody.slice(sectionStart);
223-const nextHeading = rest.match(/\n#{1,6}\s+\S/);
224-sections.push((nextHeading ? rest.slice(0, nextHeading.index) : rest).trim());
238+const matcher = new RegExp(headingRegex.source, headingRegex.flags.replaceAll("g", ""));
239+let fenceMarker = "";
240+let sectionStart = -1;
241+let lineStart = 0;
242+for (const line of normalizedBody.split("\n")) {
243+const match = !fenceMarker ? line.match(matcher) : null;
244+if (sectionStart >= 0 && !fenceMarker && isMarkdownHeadingLine(line)) {
245+sections.push(normalizedBody.slice(sectionStart, lineStart === 0 ? 0 : lineStart - 1).trim());
246+sectionStart = -1;
247+}
248+if (match) {
249+sectionStart = lineStart + (match.index ?? 0) + match[0].length;
250+}
251+fenceMarker = nextFenceMarker(line, fenceMarker);
252+lineStart += line.length + 1;
253+}
254+if (sectionStart >= 0) {
255+sections.push(normalizedBody.slice(sectionStart).trim());
225256}
226257return sections;
227258}
@@ -249,48 +280,75 @@ function fieldLineRegex(name) {
249280);
250281}
251282283+function proofFieldLineValue(line) {
284+const matchingName = allProofFieldNames.find((name) => fieldLineRegex(name).test(line));
285+const match = matchingName ? line.match(fieldLineRegex(matchingName)) : null;
286+return match?.[1] ?? null;
287+}
288+252289function isAnyProofFieldLine(line) {
253-return allProofFieldNames.some((name) => fieldLineRegex(name).test(line));
290+return proofFieldLineValue(line) !== null;
254291}
255292256293function extractFieldValue(section, field) {
257294const lines = normalizeLineEndings(section).split("\n");
295+let fenceMarker = "";
258296for (let index = 0; index < lines.length; index += 1) {
259-const matchingName = field.names.find((name) => fieldLineRegex(name).test(lines[index]));
297+const line = lines[index];
298+const matchingName = !fenceMarker
299+ ? field.names.find((name) => fieldLineRegex(name).test(line))
300+ : null;
260301if (!matchingName) {
302+const fenceLine = !fenceMarker ? (proofFieldLineValue(line) ?? line) : line;
303+fenceMarker = nextFenceMarker(fenceLine, fenceMarker);
261304continue;
262305}
263306264-const match = lines[index].match(fieldLineRegex(matchingName));
307+const match = line.match(fieldLineRegex(matchingName));
265308const valueLines = [match?.[1] ?? ""];
309+fenceMarker = nextFenceMarker(valueLines[0], "");
266310for (let next = index + 1; next < lines.length; next += 1) {
267311const line = lines[next];
268-if (/^#{1,6}\s+\S/.test(line) || isAnyProofFieldLine(line)) {
312+if (!fenceMarker && (isMarkdownHeadingLine(line) || isAnyProofFieldLine(line))) {
269313break;
270314}
271315valueLines.push(line);
316+fenceMarker = nextFenceMarker(line, fenceMarker);
272317}
273318return valueLines.join("\n").trim();
274319}
275320return "";
276321}
277322278-function stripProofFieldLabels(section) {
279-return normalizeLineEndings(section)
323+function proofContentOutsideFences(section) {
324+let fenceMarker = "";
325+const contentLines = [];
326+for (const line of normalizeLineEndings(section).split("\n")) {
327+if (fenceMarker) {
328+fenceMarker = nextFenceMarker(line, fenceMarker);
329+continue;
330+}
331+const contentLine = proofFieldLineValue(line) ?? line;
332+const nextMarker = nextFenceMarker(contentLine, fenceMarker);
333+const isFenceBoundary = nextMarker !== fenceMarker;
334+if (!isFenceBoundary) {
335+contentLines.push(contentLine);
336+}
337+fenceMarker = nextMarker;
338+}
339+return contentLines.join("\n");
340+}
341+342+function stripMarkdownFenceMarkers(value) {
343+return normalizeLineEndings(value)
280344.split("\n")
281-.map((line) => {
282-if (!isAnyProofFieldLine(line)) {
283-return line;
284-}
285-const matchingName = allProofFieldNames.find((name) => fieldLineRegex(name).test(line));
286-const match = matchingName ? line.match(fieldLineRegex(matchingName)) : null;
287-return match?.[1] ?? "";
288-})
289-.join("\n");
345+.filter((line) => !/^ {0,3}(?:`{3,}|~{3,})(?:.*)?$/.test(line))
346+.join("\n")
347+.trim();
290348}
291349292350function isMissingValue(value, field) {
293-const trimmed = value.trim();
351+const trimmed = stripMarkdownFenceMarkers(value).replace(/^\s*[-*]\s+/, "");
294352if (!trimmed) {
295353return true;
296354}
@@ -393,7 +451,7 @@ function evaluateRealBehaviorProofSection(section, body) {
393451);
394452}
395453396-const proofContent = stripProofFieldLabels(section);
454+const proofContent = proofContentOutsideFences(section);
397455if (standaloneMissingProofRegex.test(proofContent)) {
398456return result("insufficient", "Real behavior proof says the changed behavior was not tested.", {
399457 fields,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。