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 | .target(name: "OrgApp", dependencies: ["OrgWorkspace", "OrgDocument", "OrgPresentation"]), |
84 | .target(name: "OrgApp", dependencies: ["OrgWorkspace", "OrgDocument", "OrgPresentation"]), |
| 85 | .executableTarget(name: "Orgstar", dependencies: ["OrgApp", "OrgEditorAppKit", "OrgHighlight"]), |
85 | .executableTarget(name: "Orgstar", dependencies: ["OrgApp", "OrgEditorAppKit", "OrgHighlight"]), |
| 86 | // The iOS app, bundled by scripts/build-ios-app.sh. |
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 | // App extensions, bundled into Orgstar.app by scripts/build-app.sh. |
88 | // App extensions, bundled into Orgstar.app by scripts/build-app.sh. |
| 89 | .executableTarget(name: "OrgstarQuickLook", dependencies: ["OrgCore"], linkerSettings: extensionEntry), |
89 | .executableTarget(name: "OrgstarQuickLook", dependencies: ["OrgCore"], linkerSettings: extensionEntry), |
| 90 | .executableTarget(name: "OrgstarSpotlight", dependencies: ["OrgCore"], linkerSettings: extensionEntry), |
90 | .executableTarget(name: "OrgstarSpotlight", dependencies: ["OrgCore"], linkerSettings: extensionEntry), |
Sources/OrgEditorAppKit/Theme.swift
+1 −15
| @@ -118,21 +118,7 @@ struct Theme { |
| 118 | case .block: |
118 | case .block: |
| 119 | break |
119 | break |
| 120 | case .syntax(let category): |
120 | case .syntax(let category): |
| 121 | if let key = Self.slot(category) { fg(key) } |
121 | if let key = category.themeKey { 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 |
| |
| 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 | public protocol CodeHighlighter: Sendable { |
72 | public protocol CodeHighlighter: Sendable { |
| 56 | /// Categories for ranges of `code`, in UTF-16 offsets; later runs win over earlier ones. |
73 | /// Categories for ranges of `code`, in UTF-16 offsets; later runs win over earlier ones. |
| 57 | /// Empty for languages it doesn't know. |
74 | /// Empty for languages it doesn't know. |
Sources/OrgstarMobile/MobileEditor.swift
+4 −1
| @@ -1,6 +1,7 @@ |
| 1 | #if os(iOS) |
1 | #if os(iOS) |
| 2 | import OrgCore |
2 | import OrgCore |
| 3 | import OrgDocument |
3 | import OrgDocument |
| |
4 | import OrgHighlight |
| 4 | import OrgPresentation |
5 | import OrgPresentation |
| 5 | import UIKit |
6 | import UIKit |
| 6 | |
7 | |
| @@ -96,6 +97,8 @@ final class KeyedTextView: UITextView { |
| 96 | /// fold as TAB cycles them. |
97 | /// fold as TAB cycles them. |
| 97 | @MainActor |
98 | @MainActor |
| 98 | final class MobileEditor: NSObject, DocumentBuffer { |
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 | private(set) var document: DocumentState |
102 | private(set) var document: DocumentState |
| 100 | let textView: UITextView |
103 | let textView: UITextView |
| 101 | var options: EditingOptions = .org { |
104 | var options: EditingOptions = .org { |
| @@ -555,7 +558,7 @@ final class MobileEditor: NSObject, DocumentBuffer { |
| 555 | } |
558 | } |
| 556 | let revealed = caretLine |
559 | let revealed = caretLine |
| 557 | var scriptExclusions: [Range<Int>] = [] |
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 | let clipped = NSIntersectionRange(NSRange(run.range), lines) |
562 | let clipped = NSIntersectionRange(NSRange(run.range), lines) |
| 560 | guard clipped.length > 0 else { continue } |
563 | guard clipped.length > 0 else { continue } |
| 561 | if Presentation.skipsLeadingStars(run.role, indented: indented, hideStars: hideStars) { continue } |
564 | if Presentation.skipsLeadingStars(run.role, indented: indented, hideStars: hideStars) { continue } |
Sources/OrgstarMobile/MobileTheme.swift
+2 −1
| @@ -115,7 +115,8 @@ struct MobileTheme { |
| 115 | case .footnote, .cookie, .target, .macro, .latex: fg("special") |
115 | case .footnote, .cookie, .target, .macro, .latex: fg("special") |
| 116 | case .table, .rule: fg("table") |
116 | case .table, .rule: fg("table") |
| 117 | case .block: break |
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 | |