summaryrefslogtreecommitdiff
path: root/Hutch/Views/Repositories/MarkdownHTMLRenderer.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-08-07 02:45:31 -0500
committerChristian Cleberg <[email protected]>2026-08-07 02:45:31 -0500
commitb410fd16b76146f4116538c5b82330818dd47da9 (patch)
tree649488b2e5d3d901db8c792c0dc215c835fa9d38 /Hutch/Views/Repositories/MarkdownHTMLRenderer.swift
parent0a3e55c15cfb9ce1bd3df0c7b352760bc2ee33c3 (diff)
downloadhutch-b410fd16b76146f4116538c5b82330818dd47da9.tar.gz
hutch-b410fd16b76146f4116538c5b82330818dd47da9.tar.bz2
hutch-b410fd16b76146f4116538c5b82330818dd47da9.zip
Add multi-language syntax highlighting (#16)
Splash (Swift-only, since removed) left code rendering unhighlighted everywhere. Add Highlightr (highlight.js) behind a small SyntaxHighlighter service and use it on both surfaces: - Native file viewer: highlight NSAttributedString by language inferred from the filename, picking a light/dark theme from the trait style and re-highlighting on style changes. Falls back to plain text for unknown languages or very large files. - Markdown/org code blocks in the WKWebView (READMEs, tickets, comments): the web view disables JavaScript, so highlight server-side into inline color-styled spans. The color scheme is now part of the render/cache key since colors are baked into the HTML. Covered by unit tests for language mapping and highlighted output.
Diffstat (limited to 'Hutch/Views/Repositories/MarkdownHTMLRenderer.swift')
-rw-r--r--Hutch/Views/Repositories/MarkdownHTMLRenderer.swift19
1 files changed, 16 insertions, 3 deletions
diff --git a/Hutch/Views/Repositories/MarkdownHTMLRenderer.swift b/Hutch/Views/Repositories/MarkdownHTMLRenderer.swift
index bc63f9d..91166ac 100644
--- a/Hutch/Views/Repositories/MarkdownHTMLRenderer.swift
+++ b/Hutch/Views/Repositories/MarkdownHTMLRenderer.swift
@@ -2,11 +2,16 @@ import Markdown
nonisolated func markdownToHTML(
_ text: String,
+ codeTheme: SyntaxHighlightTheme = .light,
imageURLResolver: ((String) -> String?)? = nil,
linkURLResolver: ((String) -> String?)? = nil
) -> String {
let document = Document(parsing: text)
- var renderer = MarkdownHTMLRenderer(imageURLResolver: imageURLResolver, linkURLResolver: linkURLResolver)
+ var renderer = MarkdownHTMLRenderer(
+ imageURLResolver: imageURLResolver,
+ linkURLResolver: linkURLResolver,
+ highlighter: SyntaxHighlighter(theme: codeTheme)
+ )
return renderer.visit(document)
}
@@ -15,13 +20,19 @@ private struct MarkdownHTMLRenderer: MarkupVisitor {
nonisolated(unsafe) let imageURLResolver: ((String) -> String?)?
nonisolated(unsafe) let linkURLResolver: ((String) -> String?)?
+ nonisolated(unsafe) private let highlighter: SyntaxHighlighter
nonisolated(unsafe) private var isRenderingTableHead = false
nonisolated(unsafe) private var currentTableAlignments: [Markdown.Table.ColumnAlignment?] = []
nonisolated(unsafe) private var currentTableColumnIndex = 0
- nonisolated init(imageURLResolver: ((String) -> String?)?, linkURLResolver: ((String) -> String?)? = nil) {
+ nonisolated init(
+ imageURLResolver: ((String) -> String?)?,
+ linkURLResolver: ((String) -> String?)? = nil,
+ highlighter: SyntaxHighlighter
+ ) {
self.imageURLResolver = imageURLResolver
self.linkURLResolver = linkURLResolver
+ self.highlighter = highlighter
}
nonisolated mutating func visit(_ markup: Markup) -> String {
@@ -78,7 +89,9 @@ private struct MarkdownHTMLRenderer: MarkupVisitor {
} else {
classAttribute = ""
}
- return "<pre><code\(classAttribute)>\(escapeHTML(codeBlock.code))</code></pre>\n"
+ let inner = highlighter.highlightedHTML(for: codeBlock.code, language: codeBlock.language)
+ ?? escapeHTML(codeBlock.code)
+ return "<pre><code\(classAttribute)>\(inner)</code></pre>\n"
}
nonisolated mutating func visitInlineCode(_ inlineCode: InlineCode) -> String {