krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

c5247f7021090358e8db70daa9ed09521e9f206a

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-07-16T00:29:57Z

fix: render code span contents literally

processInline protected allowlisted HTML tags before it handled code spans, so
a `<b>` written inside backticks was carried through as a live tag and applied
formatting instead of rendering as text. Every other inline pass ran against
code span contents for the same reason, so `**x**` in backticks was emitted as
bold.

Protect code spans first with their contents escaped, which takes them out of
reach of the tag, emphasis, and link passes.
 Hutch/Views/Repositories/ReadmeView.swift | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/Hutch/Views/Repositories/ReadmeView.swift b/Hutch/Views/Repositories/ReadmeView.swift
index 720dff0..69d105b 100644
--- a/Hutch/Views/Repositories/ReadmeView.swift
+++ b/Hutch/Views/Repositories/ReadmeView.swift
@@ -371,8 +371,21 @@ nonisolated func processInline(
 ) -> String {
 
     var protectedFragments: [String: String] = [:]
+
+    // Code spans render their contents literally, so they have to be taken out of
+    // the text before any later pass can treat those contents as markup — the tag
+    // pass below would otherwise promote an allowlisted `<b>` into a live tag.
     var result = protectMatches(
         in: text,
+        pattern: #"`([^`]+)`"#,
+        protectedFragments: &protectedFragments
+    ) { match, nsText in
+        let code = nsText.substring(with: match.range(at: 1))
+        return "<code>\(escapeHTML(code))</code>"
+    }
+
+    result = protectMatches(
+        in: result,
         pattern: #"</?[A-Za-z][^>]*?>"#,
         protectedFragments: &protectedFragments
     ) { match, nsText in
@@ -438,13 +451,6 @@ nonisolated func processInline(
         with: "<em>$1</em>",
         options: .regularExpression
     )
-    // Inline code: `text`
-    result = result.replacingOccurrences(
-        of: #"`([^`]+)`"#,
-        with: "<code>$1</code>",
-        options: .regularExpression
-    )
-
     for (token, fragment) in protectedFragments {
         result = result.replacingOccurrences(of: token, with: fragment)
     }