Repository navigation
fix(server): PR watch wakes quote a bot comment's text, not its HTML #37032
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -261,9 +261,81 @@ export function evaluatePullRequestWatch( | |
| }; | ||
| } | ||
|
|
||
| // GitHub renders these, so in a remark's prose they are presentation, not what the reviewer said. | ||
| // Lowercase only: `Promise<T>` is a type argument. | ||
| const TAG_NAMES = | ||
| "a|abbr|b|blockquote|br|caption|cite|code|dd|del|details|div|dl|dt|em|h[1-6]|hr|i|img|ins|kbd|li|mark|ol|p|picture|pre|q|s|samp|small|source|span|strike|strong|sub|summary|sup|table|tbody|td|tfoot|th|thead|tr|tt|ul|var"; | ||
| // A code span comes first so its tags stay literal; it cannot cross a blank line. A tag stops at | ||
| // the next `<`, which keeps unclosed openers from rescanning the rest of the body. | ||
| const PROSE_MARKUP = new RegExp( | ||
| "(?<!`)(?<ticks>`+)(?!`)(?:(?!\\n[ \\t\\r]*\\n)[\\s\\S])*?(?<!`)\\k<ticks>(?!`)" + | ||
| `|<(?<closing>/?)(?<name>${TAG_NAMES})(?=[\\s/>])(?:[^<>"']|"[^"]*"|'[^']*')*>`, | ||
| "g", | ||
| ); | ||
|
|
||
| function withoutTags(prose: string): string { | ||
| // An opener glued to an identifier is a type argument (`Vec<code>`); it and its closer stay. | ||
| const kept = new Map<string, number>(); | ||
| let text = ""; | ||
| let from = 0; | ||
| for (const match of prose.matchAll(PROSE_MARKUP)) { | ||
| const name = match.groups?.name; | ||
| if (name === undefined) continue; | ||
| const held = kept.get(name) ?? 0; | ||
| if (match.groups?.closing === "/") { | ||
| if (held > 0) { | ||
| kept.set(name, held - 1); | ||
| continue; | ||
| } | ||
| } else if (/\w/.test(prose.charAt(match.index - 1))) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do not preserve attributed tags as type arguments. For Restrict type-argument preservation to attribute-free forms. Keep cases such as
|
||
| kept.set(name, held + 1); | ||
| continue; | ||
| } | ||
| text += `${prose.slice(from, match.index)} `; | ||
| from = match.index + match[0].length; | ||
| } | ||
| return text + prose.slice(from); | ||
| } | ||
|
|
||
| // Drops HTML tags from Markdown prose. Fenced and indented code blocks pass through untouched. | ||
| function withoutMarkup(body: string): string { | ||
| const parts: Array<string> = []; | ||
| let prose: Array<string> = []; | ||
| let fence: string | undefined; | ||
| let afterText = false; | ||
| const code = (line: string) => { | ||
| if (prose.length > 0) parts.push(withoutTags(prose.join("\n"))); | ||
| prose = []; | ||
| parts.push(line); | ||
| afterText = false; | ||
| }; | ||
| for (const line of body.split("\n")) { | ||
| if (fence !== undefined) { | ||
| const closer = line.trim(); | ||
| if (closer.length >= fence.length && closer.replaceAll(fence.charAt(0), "") === "") { | ||
|
Comment on lines
+314
to
+315
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium A fence indented by four or more spaces closes the code block, so following literal HTML such as - const closer = line.trim();
- if (closer.length >= fence.length && closer.replaceAll(fence.charAt(0), "") === "") {
+ const indent = line.match(/^ */)?.[0].length ?? 0;
+ const closer = line.trim();
+ if (indent <= 3 && closer.length >= fence.length && closer.replaceAll(fence.charAt(0), "") === "") {🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
Comment on lines
+314
to
+315
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check indentation before accepting a closing fence. For CommonMark permits at most three leading spaces on a closing fence. Validate the original line's indentation before clearing
|
||
| fence = undefined; | ||
| } | ||
| code(line); | ||
| continue; | ||
| } | ||
| const opener = /^ {0,3}(`{3,}|~{3,})([\s\S]*)$/.exec(line); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Medium Fenced code inside a block quote is treated as prose, so 🚀 Reply "fix it for me" or copy this AI Prompt for your agent: |
||
| if (opener?.[1] !== undefined && !(opener[1].startsWith("`") && opener[2]?.includes("`"))) { | ||
| fence = opener[1]; | ||
| code(line); | ||
| } else if (!afterText && /^(?: {4}| {0,3}\t)\s*\S/.test(line)) { | ||
| // Indented code needs a blank line before it; after text the indent is a continuation. | ||
| code(line); | ||
| } else { | ||
| prose.push(line); | ||
| afterText = line.trim() !== ""; | ||
| } | ||
| } | ||
| if (prose.length > 0) parts.push(withoutTags(prose.join("\n"))); | ||
| return parts.join("\n"); | ||
| } | ||
|
|
||
| function snippet(body: string): string { | ||
| const text = body | ||
| .replaceAll(/<!--[\s\S]*?-->/g, " ") | ||
| const text = withoutMarkup(body.replaceAll(/<!--[\s\S]*?-->/g, " ")) | ||
| .replaceAll(/\s+/g, " ") | ||
| .trim(); | ||
| if (text.length <= SNIPPET_LENGTH) return text; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 Medium
orchestration-v2/pullRequestWatch.ts:290An escaped opener such as
\<br/>is stripped from the snippet, soUse \<br/> literally.becomesUse \ literally.and omits the element being discussed.PROSE_MARKUPmatches the opener, and this check only preserves identifier-glued openers; also preserve openers preceded by an odd number of backslashes.🚀 Reply "fix it for me" or copy this AI Prompt for your agent: