krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Commit decaf5db8c

decaf5db8c55edebdb66cae18bbb466a4989ecc8

parent: 064f21f012

Verified · cmc

cmc <hello@cleberg.net> · 2026-10-05 02:57 UTC

Share paragraph styles across lines

Layout: unified · split

Sources/OrgEditorAppKit/OrgEditor.swift +17 −2
@@ -59,6 +59,13 @@ public final class OrgEditor: NSObject {
5959 /// passes reach it. Always at a line start.
6060 private(set) var styledUpTo = 0
6161 private var stylingScheduled = false
62 /// One paragraph style object per indent pair, shared by every line that uses it.
63 private var paragraphStyles: [IndentKey: NSParagraphStyle] = [:]
64
65 private struct IndentKey: Hashable {
66 let firstLine: Int
67 let wrapped: Int
68 }
6269
6370 /// Styled before the first render: more than a screenful.
6471 static let firstStyleChunk = 30_000
@@ -253,7 +260,7 @@ public final class OrgEditor: NSObject {
253260 for run in Presentation.indents(document.tree, in: range) {
254261 guard let clipped = clip(run.range, to: range) else { continue }
255262 guard run.firstLine == run.wrapped else {
256 storage.addAttribute(.paragraphStyle, value: theme.paragraphStyle(firstLine: run.firstLine, wrapped: run.wrapped), range: clipped)
263 storage.addAttribute(.paragraphStyle, value: paragraphStyle(run.firstLine, run.wrapped), range: clipped)
257264 continue
258265 }
259266 // Body lines wrap under their own text: past leading blanks, and past a list bullet
@@ -263,7 +270,7 @@ public final class OrgEditor: NSObject {
263270 let line = text.lineRange(for: NSRange(location: location, length: 0))
264271 let hanging = Self.hangingColumns(text.substring(with: line))
265272 let lineRange = NSIntersectionRange(line, clipped)
266 storage.addAttribute(.paragraphStyle, value: theme.paragraphStyle(firstLine: run.firstLine, wrapped: run.wrapped + hanging), range: lineRange)
273 storage.addAttribute(.paragraphStyle, value: paragraphStyle(run.firstLine, run.wrapped + hanging), range: lineRange)
267274 location = NSMaxRange(line)
268275 }
269276 }
@@ -273,6 +280,14 @@ public final class OrgEditor: NSObject {
273280 }
274281 }
275282
283 private func paragraphStyle(_ firstLine: Int, _ wrapped: Int) -> NSParagraphStyle {
284 let key = IndentKey(firstLine: firstLine, wrapped: wrapped)
285 if let style = paragraphStyles[key] { return style }
286 let style = theme.paragraphStyle(firstLine: firstLine, wrapped: wrapped)
287 paragraphStyles[key] = style
288 return style
289 }
290
276291 private func clip(_ run: Range<Int>, to range: Range<Int>) -> NSRange? {
277292 let clipped = run.clamped(to: range)
278293 return clipped.isEmpty ? nil : NSRange(clipped)