Files
wehub-resource-sync 5b99bf6bca
CI / test (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:34:54 +08:00

50 lines
1.8 KiB
Swift

import Foundation
/// Incremental backing for the document-global list indent unit.
///
/// Replicates `EditorTextView.detectListIndentUnit` exactly — any
/// tab-indented list line forces 4; otherwise the smallest space indent of
/// any list line; 4 when none — but as a histogram that can be updated per
/// block on the edit path instead of rescanning the whole document per
/// keystroke. Block contents tile the document's lines exactly (merged
/// constructs keep their inner newlines), so adding/removing block contents
/// is equivalent to rescanning those lines.
struct ListIndentState {
private(set) var tabLines = 0
private(set) var histogram: [Int: Int] = [:]
var unit: Int {
if tabLines > 0 { return 4 }
return histogram.keys.min() ?? 4
}
mutating func add(_ content: String) { scan(content, sign: 1) }
mutating func remove(_ content: String) { scan(content, sign: -1) }
static func build(from source: String) -> ListIndentState {
var state = ListIndentState()
state.add(source)
return state
}
private mutating func scan(_ content: String, sign: Int) {
for line in content.split(separator: "\n", omittingEmptySubsequences: false) {
var spaces = 0
var sawTab = false
for ch in line {
if ch == " " { spaces += 1 }
else if ch == "\t" { sawTab = true; break }
else { break }
}
let rest = line.drop(while: { $0 == " " || $0 == "\t" })
guard EditorTextView.startsWithListMarker(rest) else { continue }
if sawTab {
tabLines += sign
} else if spaces > 0 {
let count = (histogram[spaces] ?? 0) + sign
histogram[spaces] = count <= 0 ? nil : count
}
}
}
}