Commit decaf5db8c
Verified · cmc
Layout: unified · split
Sources/OrgEditorAppKit/OrgEditor.swift +17 −2
| @@ -59,6 +59,13 @@ public final class OrgEditor: NSObject { | ||
| 59 | 59 | /// passes reach it. Always at a line start. |
| 60 | 60 | private(set) var styledUpTo = 0 |
| 61 | 61 | 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 | } | |
| 62 | 69 | |
| 63 | 70 | /// Styled before the first render: more than a screenful. |
| 64 | 71 | static let firstStyleChunk = 30_000 |
| @@ -253,7 +260,7 @@ public final class OrgEditor: NSObject { | ||
| 253 | 260 | for run in Presentation.indents(document.tree, in: range) { |
| 254 | 261 | guard let clipped = clip(run.range, to: range) else { continue } |
| 255 | 262 | 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) | |
| 257 | 264 | continue |
| 258 | 265 | } |
| 259 | 266 | // Body lines wrap under their own text: past leading blanks, and past a list bullet |
| @@ -263,7 +270,7 @@ public final class OrgEditor: NSObject { | ||
| 263 | 270 | let line = text.lineRange(for: NSRange(location: location, length: 0)) |
| 264 | 271 | let hanging = Self.hangingColumns(text.substring(with: line)) |
| 265 | 272 | 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) | |
| 267 | 274 | location = NSMaxRange(line) |
| 268 | 275 | } |
| 269 | 276 | } |
| @@ -273,6 +280,14 @@ public final class OrgEditor: NSObject { | ||
| 273 | 280 | } |
| 274 | 281 | } |
| 275 | 282 | |
| 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 | ||
| 276 | 291 | private func clip(_ run: Range<Int>, to range: Range<Int>) -> NSRange? { |
| 277 | 292 | let clipped = run.clamped(to: range) |
| 278 | 293 | return clipped.isEmpty ? nil : NSRange(clipped) |