Skip to content

fix(markdown): preserve HTML in fenced code blocks - #720

Merged
jdx merged 2 commits into
jdx:mainfrom
risu729:agent/fix-markdown-fenced-code-escaping
Jul 20, 2026
Merged

jdx merged 2 commits into
jdx:mainfrom
risu729:agent/fix-markdown-fenced-code-escaping

Conversation

@risu729

@risu729 risu729 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

Preserve literal < characters inside conventional fenced code blocks while continuing to HTML-encode them in surrounding Markdown.

The escape filter previously processed each line independently, so it could protect inline and indented code but could not tell when a line belonged to a multiline fenced block.

This change deliberately supports the fence shape generated by replace_code_fences: column-zero fences beginning with exactly three backticks, optional info strings, and closing fences with optional trailing whitespace. It does not attempt to implement the full Markdown specification.

After rebasing, CI also exposed Rust 1.97 Clippy drift in three existing formatting expressions. The follow-up commit accepts exactly those no-behavior autofixes so the render cleanliness assertion remains stable.

Supersedes #386.
Addresses the escaping problem reported in jdx/mise#6949.

Validation

  • mise run render (clean worktree afterward)
  • mise run test
  • mise run lint

@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@risu729, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 19 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: c422ee9f-e059-4716-bc55-cd271601b21d

📥 Commits

Reviewing files that changed from the base of the PR and between c48665e and fb0b4dd.

📒 Files selected for processing (3)
  • lib/src/docs/markdown/renderer.rs
  • lib/src/parse.rs
  • lib/src/spec/mount.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes HTML escaping inside fenced code blocks by lifting the per-line escape_md logic into a stateful function that tracks open/close fence boundaries. It also includes three Clippy autofix commits (removing redundant & borrows in format! calls) that were needed to keep the CI render-cleanliness assertion passing on Rust 1.97.

  • renderer.rs: The new escape_md function iterates lines while toggling in_fenced_code_block; it recognises column-zero triple-backtick fences (with optional info strings) as openers and backtick-only lines (optional trailing whitespace) as closers, leaving all content between them unescaped. Five targeted unit tests cover the main cases.
  • parse.rs / mount.rs: Mechanical Clippy lints — redundant & in format! arguments removed, no behaviour change.

Confidence Score: 5/5

Safe to merge — the change is narrowly scoped to a well-tested escape filter, and the Clippy fixes are no-op reformats.

The stateful fence-tracking logic is straightforward and its edge cases (info strings, trailing whitespace on closing fences, indented lines, longer fences, unclosed fences) are each covered by a dedicated unit test. The opening/closing fence heuristics deliberately match only the shape emitted by replace_code_fences, which is documented in comments. The Clippy changes in parse.rs and mount.rs carry no behavioural risk.

No files require special attention.

Important Files Changed

Filename Overview
lib/src/docs/markdown/renderer.rs Extracts escape logic into escape_md function that now tracks fenced-code-block state across lines; adds five unit tests covering the main cases.
lib/src/parse.rs Clippy autofixes: removes redundant borrows in two format! argument positions.
lib/src/spec/mount.rs Clippy autofix: removes redundant borrow in format! argument.

Reviews (3): Last reviewed commit: "chore: apply clippy fixes" | Re-trigger Greptile

Comment thread lib/src/docs/markdown/renderer.rs Outdated
Comment thread lib/src/docs/markdown/renderer.rs
@risu729
risu729 force-pushed the agent/fix-markdown-fenced-code-escaping branch from 1bcee65 to faee02a Compare July 16, 2026 20:30
@risu729
risu729 marked this pull request as ready for review July 16, 2026 20:41
@jdx
jdx merged commit a24dc64 into jdx:main Jul 20, 2026
5 checks passed
@risu729
risu729 deleted the agent/fix-markdown-fenced-code-escaping branch July 21, 2026 00:01
mise-en-dev added a commit that referenced this pull request Jul 21, 2026
### 🐛 Bug Fixes

- **(cli)** avoid trailing semicolon in macro expression position by
[@jdx](https://github.com/jdx) in
[#729](#729)
- **(completion)** write spec cache to private dir instead of
world-writable tmp by [@jdx](https://github.com/jdx) in
[#727](#727)
- **(lib)** remove needless borrows in format args by
[@jdx](https://github.com/jdx) in
[#726](#726)
- **(markdown)** preserve HTML in fenced code blocks by
[@risu729](https://github.com/risu729) in
[#720](#720)
- **(nu)** create completion cache dir with mode 700 and fix home-dir
lookup by [@jdx](https://github.com/jdx) in
[#731](#731)

### 🔍 Other Changes

- remove dtolnay/rust-toolchain action, use runner default rust by
[@jdx](https://github.com/jdx) in
[#724](#724)
- regenerate aube-lock.yaml on renovate branches by
[@jdx](https://github.com/jdx) in
[#728](#728)
- exclude aube-lock.yaml from prettier by [@jdx](https://github.com/jdx)
in [#732](#732)

### 📦️ Dependency Updates

- lock file maintenance by
[@renovate[bot]](https://github.com/renovate[bot]) in
[#723](#723)
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Jul 22, 2026
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [usage](https://github.com/jdx/usage) | patch | `3.5.5` → `3.5.6` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>jdx/usage (usage)</summary>

### [`v3.5.6`](https://github.com/jdx/usage/blob/HEAD/CHANGELOG.md#356---2026-07-20)

[Compare Source](jdx/usage@v3.5.5...v3.5.6)

##### 🐛 Bug Fixes

- **(cli)** avoid trailing semicolon in macro expression position by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;729](jdx/usage#729)
- **(completion)** write spec cache to private dir instead of world-writable tmp by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;727](jdx/usage#727)
- **(lib)** remove needless borrows in format args by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;726](jdx/usage#726)
- **(markdown)** preserve HTML in fenced code blocks by [@&#8203;risu729](https://github.com/risu729) in [#&#8203;720](jdx/usage#720)
- **(nu)** create completion cache dir with mode 700 and fix home-dir lookup by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;731](jdx/usage#731)

##### 🔍 Other Changes

- remove dtolnay/rust-toolchain action, use runner default rust by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;724](jdx/usage#724)
- regenerate aube-lock.yaml on renovate branches by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;728](jdx/usage#728)
- exclude aube-lock.yaml from prettier by [@&#8203;jdx](https://github.com/jdx) in [#&#8203;732](jdx/usage#732)

##### 📦️ Dependency Updates

- lock file maintenance by [@&#8203;renovate\[bot\]](https://github.com/renovate\[bot]) in [#&#8203;723](jdx/usage#723)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever MR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNzIuNiIsInVwZGF0ZWRJblZlciI6IjQzLjI3Mi42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiLCJhdXRvbWF0aW9uOmJvdC1hdXRob3JlZCIsImRlcGVuZGVuY3ktdHlwZTo6cGF0Y2giXX0=-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants