summaryrefslogtreecommitdiff
path: root/Hutch/Views/Repositories/FileTreeView.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/FileTreeView.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/FileTreeView.swift')
-rw-r--r--Hutch/Views/Repositories/FileTreeView.swift49
1 files changed, 32 insertions, 17 deletions
diff --git a/Hutch/Views/Repositories/FileTreeView.swift b/Hutch/Views/Repositories/FileTreeView.swift
index 7bfce17..13f72b8 100644
--- a/Hutch/Views/Repositories/FileTreeView.swift
+++ b/Hutch/Views/Repositories/FileTreeView.swift
@@ -456,12 +456,19 @@ final class CodeFileUIView: UIView {
private var wrapLines: Bool
private var needsLineLayoutUpdate = true
private var lastMeasuredCodeWidth: CGFloat = 0
+ private var syntaxHighlighter: SyntaxHighlighter?
+ private var syntaxHighlighterTheme: SyntaxHighlightTheme?
init(text: String, fileName: String, font: UIFont, wrapLines: Bool) {
self.currentFont = font
self.wrapLines = wrapLines
super.init(frame: .zero)
setupViews()
+ registerForTraitChanges([UITraitUserInterfaceStyle.self]) { (view: CodeFileUIView, _: UITraitCollection) in
+ view.rebuildRows()
+ view.needsLineLayoutUpdate = true
+ view.setNeedsLayout()
+ }
updateContent(text: text, fileName: fileName, font: font, wrapLines: wrapLines)
}
@@ -583,11 +590,7 @@ final class CodeFileUIView: UIView {
}
rows.removeAll()
- let attributedText = CodeSyntaxHighlighter.attributedText(
- for: currentText,
- fileName: currentFileName,
- font: currentFont
- )
+ let attributedText = highlightedAttributedText()
let lines = makeLines(from: attributedText, font: currentFont)
gutterWidthConstraint?.constant = Self.gutterWidth(lineCount: lines.count, font: currentFont)
@@ -600,6 +603,30 @@ final class CodeFileUIView: UIView {
}
}
+ /// Highlighting cap: above this size, skip tokenizing to keep the main
+ /// thread responsive and render plain, label-colored text instead.
+ private static let maxHighlightableLength = 100_000
+
+ private func highlightedAttributedText() -> NSAttributedString {
+ let plain = NSAttributedString(
+ string: currentText,
+ attributes: [.font: currentFont, .foregroundColor: UIColor.label]
+ )
+
+ guard currentText.count <= Self.maxHighlightableLength,
+ let language = SyntaxHighlighter.language(forFileName: currentFileName) else {
+ return plain
+ }
+
+ let theme = SyntaxHighlightTheme(userInterfaceStyle: traitCollection.userInterfaceStyle)
+ if syntaxHighlighterTheme != theme || syntaxHighlighter == nil {
+ syntaxHighlighter = SyntaxHighlighter(theme: theme)
+ syntaxHighlighterTheme = theme
+ }
+
+ return syntaxHighlighter?.attributedText(for: currentText, language: language, font: currentFont) ?? plain
+ }
+
private func makeRow(for line: CodeFileLineData) -> LineRow {
let row = LineRow()
@@ -737,18 +764,6 @@ private struct CodeFileLineData {
let text: NSAttributedString
}
-private enum CodeSyntaxHighlighter {
- static func attributedText(for text: String, fileName _: String, font: UIFont) -> NSAttributedString {
- NSAttributedString(
- string: text,
- attributes: [
- .font: font,
- .foregroundColor: UIColor.label
- ]
- )
- }
-}
-
// MARK: - Tree Entry Row
private struct TreeEntryRow: View {