a native ios client for gitbay

client ios swift

https://gitbay.org

gitbay/Views/Repos/SyntaxHighlighter.swift

main
gitbay-ios/gitbay/Views/Repos/SyntaxHighlighter.swift history · blame · raw

87 lines · 3325 bytes

 1import Foundation
 2import Highlightr
 3import SwiftUI
 4import UIKit
 5
 6/// Adapted from hutch: Highlightr wraps a JavaScriptCore context and is
 7/// not thread-safe, so an instance stays where it was made. A `nil` result
 8/// means "show plain text", never an error.
 9nonisolated final class SyntaxHighlighter {
10
11    private let highlightr: Highlightr?
12    private let supported: Set<String>
13
14    init(colorScheme: ColorScheme) {
15        let engine = Highlightr()
16        // Xcode-like in light, muted in dark, so code reads as native.
17        engine?.setTheme(to: colorScheme == .dark ? "atom-one-dark" : "xcode")
18        highlightr = engine
19        supported = Set(engine?.supportedLanguages() ?? [])
20    }
21
22    func highlight(_ code: String, fileName: String, font: UIFont) -> NSAttributedString? {
23        guard let highlightr, let language = Self.language(for: fileName),
24              supported.contains(language) else {
25            return nil
26        }
27        highlightr.theme.setCodeFont(font)
28        return highlightr.highlight(code, as: language, fastRender: true)
29    }
30
31    /// File extension  highlight.js language name. Unknown is fine; the
32    /// caller falls back to plain text.
33    static func language(for fileName: String) -> String? {
34        let name = fileName.lowercased()
35        if name == "makefile" { return "makefile" }
36        if name == "dockerfile" { return "dockerfile" }
37        guard let ext = name.split(separator: ".").last, ext != name else { return nil }
38        switch ext {
39        case "swift": return "swift"
40        case "go": return "go"
41        case "rs": return "rust"
42        case "py": return "python"
43        case "rb": return "ruby"
44        case "js", "mjs", "cjs": return "javascript"
45        case "ts", "tsx": return "typescript"
46        case "jsx": return "javascript"
47        case "c", "h": return "c"
48        case "cc", "cpp", "cxx", "hpp": return "cpp"
49        case "m": return "objectivec"
50        case "cs": return "csharp"
51        case "java": return "java"
52        case "kt", "kts": return "kotlin"
53        case "sh", "bash", "zsh": return "bash"
54        case "fish": return "shell"
55        case "php": return "php"
56        case "pl": return "perl"
57        case "lua": return "lua"
58        case "html", "htm": return "xml"
59        case "xml", "plist", "storyboard", "xib": return "xml"
60        case "css": return "css"
61        case "scss", "sass": return "scss"
62        case "json": return "json"
63        case "yml", "yaml": return "yaml"
64        case "toml", "ini", "cfg": return "ini"
65        case "md", "markdown": return "markdown"
66        case "org": return "plaintext"
67        case "sql": return "sql"
68        case "diff", "patch": return "diff"
69        case "proto": return "protobuf"
70        case "cmake": return "cmake"
71        case "gradle": return "gradle"
72        case "ex", "exs": return "elixir"
73        case "erl": return "erlang"
74        case "hs": return "haskell"
75        case "ml", "mli": return "ocaml"
76        case "clj", "cljs": return "clojure"
77        case "scala": return "scala"
78        case "r": return "r"
79        case "dart": return "dart"
80        case "zig": return "plaintext"
81        case "vim": return "vim"
82        case "tex": return "latex"
83        case "txt", "text", "log": return nil
84        default: return nil
85        }
86    }
87}