krz/orgstar

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

Commit 114c90bd03

114c90bd03b49f064eec93f52fe2c86fab699129

parent: f36736932d

Verified · cmc

cmc <hello@cleberg.net> · 2026-10-06 00:13 UTC

Keep body folds through edits

The edit path rebuilt hidden ranges and folded headings from folds only,
so bodies hidden by the CONTENTS view came back after any keystroke
while the editor still counted them folded. It now updates both the way
folding does.

Layout: unified · split

Sources/OrgEditorAppKit/OrgEditor.swift +1 −2
@@ -727,8 +727,7 @@ public final class OrgEditor: NSObject {
727 } 727 }
728 let oldHidden = hidden.all 728 let oldHidden = hidden.all
729 view = view.mapped(through: [edit]).pruned(to: document.tree) 729 view = view.mapped(through: [edit]).pruned(to: document.tree)
730 folded.set(view.folds) 730 updateHidden()
731 hidden.set(Presentation.hiddenRanges(document.tree, folds: view.folds))
732 let newAffected = Self.affectedRange(document.tree, newText, edit.range.lowerBound, newEnd, touchedHeadings: touchedHeadings) 731 let newAffected = Self.affectedRange(document.tree, newText, edit.range.lowerBound, newEnd, touchedHeadings: touchedHeadings)
733 let delta = edit.replacement.utf16.count - edit.range.count 732 let delta = edit.replacement.utf16.count - edit.range.count
734 let oldMapped = oldAffected.lowerBound..<(oldAffected.upperBound >= edit.range.upperBound ? oldAffected.upperBound + delta : newEnd) 733 let oldMapped = oldAffected.lowerBound..<(oldAffected.upperBound >= edit.range.upperBound ? oldAffected.upperBound + delta : newEnd)
Tests/OrgEditorAppKitTests/FindTests.swift +17
@@ -24,3 +24,20 @@ struct FindTests {
24 #expect(h.textView.selectedRange() == needle) 24 #expect(h.textView.selectedRange() == needle)
25 } 25 }
26} 26}
27
28@MainActor
29struct BodyFoldTests {
30 /// CONTENTS view hides bodies (body folds); an edit elsewhere keeps them hidden.
31 @Test func bodyFoldsSurviveEdits() {
32 let h = Harness("* A\nbody a\n** B\nbody b\n* C\nbody c\n")
33 h.editor.cycleGlobal()
34 h.editor.cycleGlobal()
35 #expect(!h.editor.foldState.bodyFolds.isEmpty)
36 let hiddenBefore = h.editor.hidden.all
37 #expect(!hiddenBefore.isEmpty)
38 h.caret(at: 3)
39 h.textView.insertText("x", replacementRange: NSRange(location: 3, length: 0))
40 #expect(h.editor.hidden.all.count == hiddenBefore.count)
41 #expect(h.editor.hidden.all.map { $0.count } == hiddenBefore.map { $0.count })
42 }
43}