Skip to content

Commit 24d9b7a

Browse files
committed
Begin Transition To RangeStore Model
1 parent b1511ee commit 24d9b7a

8 files changed

Lines changed: 346 additions & 241 deletions

File tree

Sources/CodeEditSourceEditor/LineFolding/Model/FoldRange.swift

Lines changed: 90 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,107 @@
77

88
import Foundation
99

10-
/// Represents a recursive folded range
10+
/// Represents a folded range
1111
class FoldRange {
12-
var lineRange: ClosedRange<Int>
1312
var range: NSRange
1413
var depth: Int
1514
var collapsed: Bool
16-
/// Ordered array of ranges that are nested in this fold.
17-
var subFolds: [FoldRange]
18-
19-
weak var parent: FoldRange?
2015

2116
init(
22-
lineRange: ClosedRange<Int>,
2317
range: NSRange,
2418
depth: Int,
25-
collapsed: Bool,
26-
parent: FoldRange?,
27-
subFolds: [FoldRange]
19+
collapsed: Bool
2820
) {
29-
self.lineRange = lineRange
3021
self.range = range
3122
self.depth = depth
3223
self.collapsed = collapsed
33-
self.subFolds = subFolds
34-
self.parent = parent
24+
}
25+
}
26+
27+
struct LineFoldStorage: Sendable {
28+
struct Fold: RangeStoreElement, Sendable {
29+
var isEmpty: Bool { depth == nil }
30+
31+
var depth: Int?
32+
var collapsed: Bool
33+
}
34+
35+
struct FoldRunInfo: Equatable, Sendable {
36+
let depth: Int
37+
let collapsed: Bool
38+
let runs: [Range<Int>]
39+
}
40+
41+
var storage: RangeStore<Fold>
42+
43+
init(foldDepths: [(range: Range<Int>, depth: Int)], documentLength: Int) {
44+
storage = RangeStore<Fold>(documentLength: documentLength)
45+
46+
for foldDepth in foldDepths {
47+
storage.set(
48+
value: Fold(depth: foldDepth.depth, collapsed: false),
49+
for: foldDepth.range
50+
)
51+
}
52+
}
53+
54+
func depth(at offset: Int) -> Int? {
55+
storage.findValue(at: offset)?.depth
56+
}
57+
58+
func foldMarkers(for range: ClosedRange<Int>) -> [FoldRange] {
59+
[]
60+
}
61+
62+
func collectRuns(forDeepestFoldAt offset: Int) -> FoldRunInfo? {
63+
let initialIndex = storage.findIndex(at: offset).index
64+
guard let foldRange = storage.findValue(at: offset),
65+
let foldDepth = foldRange.depth else {
66+
return nil
67+
}
68+
69+
var runs: [Range<Int>] = []
70+
71+
func appendRun(_ index: RangeStore<Fold>.Index) {
72+
let location = storage._guts.offset(of: index, in: RangeStore.OffsetMetric())
73+
let length = storage._guts[initialIndex].length
74+
runs.append(location..<(location + length))
75+
}
76+
appendRun(initialIndex)
77+
78+
// Collect up
79+
if initialIndex != storage._guts.startIndex {
80+
var index = storage._guts.index(before: initialIndex)
81+
while index != storage._guts.startIndex,
82+
let nextDepth = storage._guts[index].value?.depth,
83+
nextDepth >= foldDepth {
84+
if nextDepth == foldDepth {
85+
appendRun(index)
86+
}
87+
index = storage._guts.index(before: index)
88+
}
89+
}
90+
91+
// Collect down
92+
if initialIndex != storage._guts.endIndex {
93+
var index = storage._guts.index(after: initialIndex)
94+
while index != storage._guts.endIndex,
95+
let nextDepth = storage._guts[index].value?.depth,
96+
nextDepth >= foldDepth {
97+
if nextDepth == foldDepth {
98+
appendRun(index)
99+
}
100+
index = storage._guts.index(after: index)
101+
}
102+
}
103+
104+
return FoldRunInfo(depth: foldDepth, collapsed: foldRange.collapsed, runs: runs)
105+
}
106+
107+
mutating func toggleCollapse(at offset: Int) {
108+
guard let foldInfo = collectRuns(forDeepestFoldAt: offset) else { return }
109+
for run in foldInfo.runs {
110+
storage.set(value: Fold(depth: foldInfo.depth, collapsed: !foldInfo.collapsed), for: run)
111+
}
35112
}
36113
}

Sources/CodeEditSourceEditor/LineFolding/Model/LineFoldCalculator.swift

Lines changed: 63 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -47,68 +47,68 @@ class LineFoldCalculator {
4747
/// For each line in the document, find the indentation level using the ``levelProvider``. At each line, if the
4848
/// indent increases from the previous line, we start a new fold. If it decreases we end the fold we were in.
4949
private func buildFoldsForDocument(afterEditIn: NSRange, delta: Int) {
50-
workQueue.async {
51-
guard let textView = self.textView, let foldProvider = self.foldProvider else { return }
52-
var foldCache: [FoldRange] = []
53-
var currentFold: FoldRange?
54-
var currentDepth: Int = 0
55-
var iterator = textView.layoutManager.linesInRange(textView.documentRange)
56-
57-
var lines = self.getMoreLines(
58-
textView: textView,
59-
iterator: &iterator,
60-
lastDepth: currentDepth,
61-
foldProvider: foldProvider
62-
)
63-
while let lineChunk = lines {
64-
for lineInfo in lineChunk {
65-
// Start a new fold, going deeper to a new depth.
66-
if lineInfo.providerInfo.depth > currentDepth {
67-
let newFold = FoldRange(
68-
lineRange: lineInfo.lineNumber...lineInfo.lineNumber,
69-
range: NSRange(location: lineInfo.providerInfo.rangeIndice, length: 0),
70-
depth: lineInfo.providerInfo.depth,
71-
collapsed: lineInfo.collapsed,
72-
parent: currentFold,
73-
subFolds: []
74-
)
75-
76-
if currentFold == nil {
77-
foldCache.append(newFold)
78-
} else {
79-
currentFold?.subFolds.append(newFold)
80-
}
81-
currentFold = newFold
82-
} else if lineInfo.providerInfo.depth < currentDepth {
83-
// End this fold, go shallower "popping" folds deeper than the new depth
84-
while let fold = currentFold, fold.depth > lineInfo.providerInfo.depth {
85-
// close this fold at the current line
86-
fold.lineRange = fold.lineRange.lowerBound...lineInfo.lineNumber
87-
fold.range = NSRange(start: fold.range.location, end: lineInfo.providerInfo.rangeIndice)
88-
// move up
89-
currentFold = fold.parent
90-
}
91-
}
92-
93-
currentDepth = lineInfo.providerInfo.depth
94-
}
95-
lines = self.getMoreLines(
96-
textView: textView,
97-
iterator: &iterator,
98-
lastDepth: currentDepth,
99-
foldProvider: foldProvider
100-
)
101-
}
102-
103-
// Clean up any hanging folds.
104-
while let fold = currentFold {
105-
fold.lineRange = fold.lineRange.lowerBound...textView.layoutManager.lineCount - 1
106-
fold.range = NSRange(start: fold.range.location, end: textView.documentRange.length)
107-
currentFold = fold.parent
108-
}
109-
110-
self.rangesPublisher.send(foldCache)
111-
}
50+
// workQueue.async {
51+
// guard let textView = self.textView, let foldProvider = self.foldProvider else { return }
52+
// var foldCache: [FoldRange] = []
53+
// var currentFold: FoldRange?
54+
// var currentDepth: Int = 0
55+
// var iterator = textView.layoutManager.linesInRange(textView.documentRange)
56+
//
57+
// var lines = self.getMoreLines(
58+
// textView: textView,
59+
// iterator: &iterator,
60+
// lastDepth: currentDepth,
61+
// foldProvider: foldProvider
62+
// )
63+
// while let lineChunk = lines {
64+
// for lineInfo in lineChunk {
65+
// // Start a new fold, going deeper to a new depth.
66+
// if lineInfo.providerInfo.depth > currentDepth {
67+
// let newFold = FoldRange(
68+
// lineRange: lineInfo.lineNumber...lineInfo.lineNumber,
69+
// range: NSRange(location: lineInfo.providerInfo.rangeIndice, length: 0),
70+
// depth: lineInfo.providerInfo.depth,
71+
// collapsed: lineInfo.collapsed,
72+
// parent: currentFold,
73+
// subFolds: []
74+
// )
75+
//
76+
// if currentFold == nil {
77+
// foldCache.append(newFold)
78+
// } else {
79+
// currentFold?.subFolds.append(newFold)
80+
// }
81+
// currentFold = newFold
82+
// } else if lineInfo.providerInfo.depth < currentDepth {
83+
// // End this fold, go shallower "popping" folds deeper than the new depth
84+
// while let fold = currentFold, fold.depth > lineInfo.providerInfo.depth {
85+
// // close this fold at the current line
86+
// fold.lineRange = fold.lineRange.lowerBound...lineInfo.lineNumber
87+
// fold.range = NSRange(start: fold.range.location, end: lineInfo.providerInfo.rangeIndice)
88+
// // move up
89+
// currentFold = fold.parent
90+
// }
91+
// }
92+
//
93+
// currentDepth = lineInfo.providerInfo.depth
94+
// }
95+
// lines = self.getMoreLines(
96+
// textView: textView,
97+
// iterator: &iterator,
98+
// lastDepth: currentDepth,
99+
// foldProvider: foldProvider
100+
// )
101+
// }
102+
//
103+
// // Clean up any hanging folds.
104+
// while let fold = currentFold {
105+
// fold.lineRange = fold.lineRange.lowerBound...textView.layoutManager.lineCount - 1
106+
// fold.range = NSRange(start: fold.range.location, end: textView.documentRange.length)
107+
// currentFold = fold.parent
108+
// }
109+
//
110+
// self.rangesPublisher.send(foldCache)
111+
// }
112112
}
113113

114114
private func getMoreLines(
@@ -131,15 +131,11 @@ class LineFoldCalculator {
131131
count += 1
132132
continue
133133
}
134-
let attachments = textView.layoutManager.attachments
135-
.getAttachmentsOverlapping(linePosition.range)
136-
.compactMap({ $0.attachment as? LineFoldPlaceholder })
137-
138134
results.append(
139135
LineInfo(
140136
lineNumber: linePosition.index,
141137
providerInfo: foldInfo,
142-
collapsed: !attachments.isEmpty
138+
collapsed: false
143139
)
144140
)
145141
count += 1

Sources/CodeEditSourceEditor/LineFolding/Model/LineFoldingModel.swift

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ class LineFoldingModel: NSObject, NSTextStorageDelegate {
4040
}
4141

4242
func getFolds(in lineRange: ClosedRange<Int>) -> [FoldRange] {
43-
foldCache.withValue { $0.filter({ $0.lineRange.overlaps(lineRange) }) }
43+
// foldCache.withValue { $0.filter({ $0.lineRange.overlaps(lineRange) }) }
44+
[]
4445
}
4546

4647
func textStorage(
@@ -66,48 +67,9 @@ class LineFoldingModel: NSObject, NSTextStorageDelegate {
6667
/// - Parameter lineNumber: The line number to query, zero-indexed.
6768
/// - Returns: The deepest cached fold and depth of the fold if it was found.
6869
func getCachedFoldAt(lineNumber: Int) -> (range: FoldRange, depth: Int)? {
69-
foldCache.withValue { foldCache in
70-
binarySearchFoldsArray(lineNumber: lineNumber, folds: foldCache, currentDepth: 0, findDeepest: true)
71-
}
72-
}
73-
}
74-
75-
// MARK: - Search Folds
76-
77-
private extension LineFoldingModel {
78-
/// A generic function for searching an ordered array of fold ranges.
79-
/// - Returns: The found range and depth it was found at, if it exists.
80-
func binarySearchFoldsArray(
81-
lineNumber: Int,
82-
folds: borrowing [FoldRange],
83-
currentDepth: Int,
84-
findDeepest: Bool
85-
) -> (range: FoldRange, depth: Int)? {
86-
var low = 0
87-
var high = folds.count - 1
88-
89-
while low <= high {
90-
let mid = (low + high) / 2
91-
let fold = folds[mid]
92-
93-
if fold.lineRange.contains(lineNumber) {
94-
// Search deeper into subFolds, if any
95-
if findDeepest {
96-
return binarySearchFoldsArray(
97-
lineNumber: lineNumber,
98-
folds: fold.subFolds,
99-
currentDepth: currentDepth + 1,
100-
findDeepest: findDeepest
101-
) ?? (fold, currentDepth)
102-
} else {
103-
return (fold, currentDepth)
104-
}
105-
} else if lineNumber < fold.lineRange.lowerBound {
106-
high = mid - 1
107-
} else {
108-
low = mid + 1
109-
}
110-
}
111-
return nil
70+
// foldCache.withValue { foldCache in
71+
// binarySearchFoldsArray(lineNumber: lineNumber, folds: foldCache, currentDepth: 0, findDeepest: true)
72+
// }
73+
nil
11274
}
11375
}

0 commit comments

Comments
 (0)