fix: renumber sibling sequences when list indentation changes
This commit is contained in:
@@ -345,9 +345,77 @@ final class NotesController: NSViewController, NSTextViewDelegate {
|
||||
guard indenting || removed > 0 else { return true }
|
||||
let replacement = indenting ? " " : ""
|
||||
let delta = replacement.utf16.count - removed
|
||||
let selectedStart = max(start, selection.location + delta)
|
||||
let selectedEnd = max(start, NSMaxRange(selection) + delta)
|
||||
replaceListMarkdown(replacement, range: NSRange(location: start, length: removed),
|
||||
var selectedStart = max(start, selection.location + delta)
|
||||
var selectedEnd = max(start, NSMaxRange(selection) + delta)
|
||||
func isListLine(_ range: NSRange) -> Bool {
|
||||
Self.listMatch(in: source.substring(with: range) as NSString) != nil
|
||||
&& !codeBlocks.contains { NSIntersectionRange($0.range, range).length > 0 }
|
||||
}
|
||||
var block = source.lineRange(for: NSRange(location: start, length: 0))
|
||||
while block.location > 0 {
|
||||
let previous = source.lineRange(for: NSRange(location: block.location - 1, length: 0))
|
||||
guard isListLine(previous) else { break }
|
||||
block = NSUnionRange(previous, block)
|
||||
}
|
||||
while NSMaxRange(block) < source.length {
|
||||
let next = source.lineRange(for: NSRange(location: NSMaxRange(block), length: 0))
|
||||
guard isListLine(next) else { break }
|
||||
block = NSUnionRange(block, next)
|
||||
}
|
||||
let body = NSMutableAttributedString(attributedString: editor.markdownContents.attributedSubstring(from: block))
|
||||
body.replaceCharacters(in: NSRange(location: start - block.location, length: removed), with: replacement)
|
||||
let changed = body.string as NSString
|
||||
let oldDepth = Self.indentationWidth(indent)
|
||||
let newDepth = Self.indentationWidth(replacement + (indent as NSString).substring(from: removed))
|
||||
var ancestors: [(depth: Int, number: UInt64?, delimiter: String)] = []
|
||||
var edits: [(range: NSRange, number: String)] = []
|
||||
var oldLevel = true, newLevel = true
|
||||
var offset = 0
|
||||
while offset < changed.length {
|
||||
let row = changed.lineRange(for: NSRange(location: offset, length: 0))
|
||||
let text = changed.substring(with: row) as NSString
|
||||
guard let match = Self.listMatch(in: text) else { break }
|
||||
let depth = Self.indentationWidth(text.substring(with: match.range(at: 1)))
|
||||
if offset + block.location > start {
|
||||
if depth < oldDepth { oldLevel = false }
|
||||
if depth < newDepth { newLevel = false }
|
||||
}
|
||||
while let last = ancestors.last, last.depth > depth { ancestors.removeLast() }
|
||||
let peer = ancestors.last?.depth == depth ? ancestors.removeLast() : nil
|
||||
var number: UInt64?
|
||||
var delimiter = ""
|
||||
if match.range(at: 3).location != NSNotFound {
|
||||
number = UInt64(text.substring(with: match.range(at: 3)))
|
||||
delimiter = text.substring(with: match.range(at: 4))
|
||||
if offset + block.location >= start,
|
||||
(oldLevel && depth == oldDepth) || (newLevel && depth == newDepth) {
|
||||
if let peer, peer.delimiter == delimiter, let previous = peer.number, previous < UInt64.max {
|
||||
number = previous + 1
|
||||
} else if !ancestors.isEmpty || offset + block.location > start {
|
||||
number = 1
|
||||
}
|
||||
if let number, UInt64(text.substring(with: match.range(at: 3))) != number {
|
||||
let digits = match.range(at: 3)
|
||||
edits.append((NSRange(location: offset + digits.location, length: digits.length), String(number)))
|
||||
}
|
||||
}
|
||||
}
|
||||
ancestors.append((depth, number, delimiter))
|
||||
offset = NSMaxRange(row)
|
||||
}
|
||||
for edit in edits.reversed() {
|
||||
body.replaceCharacters(in: edit.range, with: edit.number)
|
||||
let location = block.location + edit.range.location
|
||||
let difference = edit.number.utf16.count - edit.range.length
|
||||
func adjusted(_ position: Int) -> Int {
|
||||
if position <= location { return position }
|
||||
if position < location + edit.range.length { return location + min(position - location, edit.number.utf16.count) }
|
||||
return position + difference
|
||||
}
|
||||
selectedStart = adjusted(selectedStart)
|
||||
selectedEnd = adjusted(selectedEnd)
|
||||
}
|
||||
replaceMarkdown(body, range: block,
|
||||
selection: NSRange(location: selectedStart, length: selectedEnd - selectedStart))
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user