Commit 745599e8e9
745599e8e905766da00da4d49c9b867ba4d6efb5
parent: 4c75f0eea5
Verified · cmc
cmc <hello@cleberg.net> · 2026-10-09 01:09 UTC
Highlight src blocks in the iOS editor
OrgstarMobile depends on OrgHighlight; the tree-sitter targets build for
iOS as they are. The syntax category to theme key mapping moves to
OrgPresentation for both editors.
Layout: unified · split
Package.swift
+1 −1
| @@ -84,7 +84,7 @@ let package = Package( |
| 84 | 84 | .target(name: "OrgApp", dependencies: ["OrgWorkspace", "OrgDocument", "OrgPresentation"]), |
| 85 | 85 | .executableTarget(name: "Orgstar", dependencies: ["OrgApp", "OrgEditorAppKit", "OrgHighlight"]), |
| 86 | 86 | // The iOS app, bundled by scripts/build-ios-app.sh. |
| 87 | | .executableTarget(name: "OrgstarMobile", dependencies: ["OrgApp"]), |
| 87 | .executableTarget(name: "OrgstarMobile", dependencies: ["OrgApp", "OrgHighlight"]), |
| 88 | 88 | // App extensions, bundled into Orgstar.app by scripts/build-app.sh. |
| 89 | 89 | .executableTarget(name: "OrgstarQuickLook", dependencies: ["OrgCore"], linkerSettings: extensionEntry), |
| 90 | 90 | .executableTarget(name: "OrgstarSpotlight", dependencies: ["OrgCore"], linkerSettings: extensionEntry), |
Sources/OrgEditorAppKit/Theme.swift
+1 −15
| @@ -118,21 +118,7 @@ struct Theme { |
| 118 | 118 | case .block: |
| 119 | 119 | break |
| 120 | 120 | case .syntax(let category): |
| 121 | | if let key = Self.slot(category) { fg(key) } |
| 122 | | } |
| 123 | | } |
| 124 | | |
| 125 | | static func slot(_ category: SyntaxCategory) -> String? { |
| 126 | | switch category { |
| 127 | | case .keyword: "syntax-keyword" |
| 128 | | case .string: "syntax-string" |
| 129 | | case .comment: "syntax-comment" |
| 130 | | case .function: "syntax-function" |
| 131 | | case .type: "syntax-type" |
| 132 | | case .number, .constant, .escape: "syntax-number" |
| 133 | | case .property, .attribute, .tag: "syntax-property" |
| 134 | | case .label: "syntax-label" |
| 135 | | case .variable, .operator, .punctuation: nil |
| 121 | if let key = category.themeKey { fg(key) } |
| 136 | 122 | } |
| 137 | 123 | } |
| 138 | 124 | |
Sources/OrgPresentation/Presentation.swift
+18 −1
| @@ -51,7 +51,24 @@ public enum SyntaxCategory: String, Hashable, Sendable, CaseIterable { |
| 51 | 51 | } |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | | /// Highlights source code; the platform supplies one (tree-sitter on macOS; iOS supplies none). |
| 54 | extension SyntaxCategory { |
| 55 | /// The theme color for this kind of token; nil for those drawn in the text color. |
| 56 | public var themeKey: String? { |
| 57 | switch self { |
| 58 | case .keyword: "syntax-keyword" |
| 59 | case .string: "syntax-string" |
| 60 | case .comment: "syntax-comment" |
| 61 | case .function: "syntax-function" |
| 62 | case .type: "syntax-type" |
| 63 | case .number, .constant, .escape: "syntax-number" |
| 64 | case .property, .attribute, .tag: "syntax-property" |
| 65 | case .label: "syntax-label" |
| 66 | case .variable, .operator, .punctuation: nil |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /// Highlights source code; the apps supply tree-sitter's (OrgHighlight). |
| 55 | 72 | public protocol CodeHighlighter: Sendable { |
| 56 | 73 | /// Categories for ranges of `code`, in UTF-16 offsets; later runs win over earlier ones. |
| 57 | 74 | /// Empty for languages it doesn't know. |
Sources/OrgstarMobile/MobileEditor.swift
+4 −1
| @@ -1,6 +1,7 @@ |
| 1 | 1 | #if os(iOS) |
| 2 | 2 | import OrgCore |
| 3 | 3 | import OrgDocument |
| 4 | import OrgHighlight |
| 4 | 5 | import OrgPresentation |
| 5 | 6 | import UIKit |
| 6 | 7 | |
| @@ -96,6 +97,8 @@ final class KeyedTextView: UITextView { |
| 96 | 97 | /// fold as TAB cycles them. |
| 97 | 98 | @MainActor |
| 98 | 99 | final class MobileEditor: NSObject, DocumentBuffer { |
| 100 | /// One highlighter for every editor and the reader: it caches compiled grammars and results. |
| 101 | static let highlighter = TreeSitterHighlighter() |
| 99 | 102 | private(set) var document: DocumentState |
| 100 | 103 | let textView: UITextView |
| 101 | 104 | var options: EditingOptions = .org { |
| @@ -555,7 +558,7 @@ final class MobileEditor: NSObject, DocumentBuffer { |
| 555 | 558 | } |
| 556 | 559 | let revealed = caretLine |
| 557 | 560 | var scriptExclusions: [Range<Int>] = [] |
| 558 | | for run in Presentation.styles(document.tree, in: whole) { |
| 561 | for run in Presentation.styles(document.tree, in: whole, highlighter: Self.highlighter) { |
| 559 | 562 | let clipped = NSIntersectionRange(NSRange(run.range), lines) |
| 560 | 563 | guard clipped.length > 0 else { continue } |
| 561 | 564 | if Presentation.skipsLeadingStars(run.role, indented: indented, hideStars: hideStars) { continue } |
Sources/OrgstarMobile/MobileTheme.swift
+2 −1
| @@ -115,7 +115,8 @@ struct MobileTheme { |
| 115 | 115 | case .footnote, .cookie, .target, .macro, .latex: fg("special") |
| 116 | 116 | case .table, .rule: fg("table") |
| 117 | 117 | case .block: break |
| 118 | | case .syntax: break |
| 118 | case .syntax(let category): |
| 119 | if let key = category.themeKey { fg(key) } |
| 119 | 120 | } |
| 120 | 121 | } |
| 121 | 122 | |