Skip to content

Commit

Permalink
Fixed issue related to hang encountered when editing some lists (#343)
Browse files Browse the repository at this point in the history
  • Loading branch information
rajdeep authored Oct 4, 2024
1 parent 486b674 commit 430435f
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 24 deletions.
20 changes: 0 additions & 20 deletions Proton/Sources/Swift/Core/LayoutManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,26 +88,6 @@ class LayoutManager: NSLayoutManager {
counters = [:]
}

var levelToSet = 0
textStorage.enumerateAttribute(.paragraphStyle, in: listRange, options: []) { value, range, _ in
levelToSet = 0
if let paraStyle = (value as? NSParagraphStyle)?.mutableParagraphStyle {
let previousLevel = Int(prevStyle?.firstLineHeadIndent ?? 0)/Int(listIndent)
let currentLevel = Int(paraStyle.firstLineHeadIndent)/Int(listIndent)

if currentLevel - previousLevel > 1 {
levelToSet = previousLevel + 1
let indentation = CGFloat(levelToSet) * listIndent
paraStyle.firstLineHeadIndent = indentation
paraStyle.headIndent = indentation
textStorage.addAttribute(.paragraphStyle, value: paraStyle, range: range)
prevStyle = paraStyle
} else {
prevStyle = value as? NSParagraphStyle
}
}
}

let listGlyphRange = glyphRange(forCharacterRange: listRange, actualCharacterRange: nil)
previousLevel = 0
enumerateLineFragments(forGlyphRange: listGlyphRange) { [weak self] (rect, usedRect, textContainer, glyphRange, stop) in
Expand Down
31 changes: 31 additions & 0 deletions Proton/Sources/Swift/EditorCommand/Commands/List/ListCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public class ListCommand: EditorCommand {
], at: selectedRange)
editor.removeAttribute(.listItem, at: selectedRange)
editor.typingAttributes[.listItem] = nil
cleanupIfNeeded(editor: editor)
return
}

Expand Down Expand Up @@ -132,4 +133,34 @@ public class ListCommand: EditorCommand {
self.attributeValue = attributeValue
execute(on: editor)
}

// Cleanup any dangling lists after the parent of this is removed as being a list item
private func cleanupIfNeeded(editor: EditorView) {
guard let nextContentLine = editor.nextContentLine(from: editor.selectedRange.endLocation),
nextContentLine.text.attributeOrNil(.listItem, at: 0) != nil,
let listToUpdateRange = editor.attributedText.rangeOf(attribute: .listItem, startingLocation: editor.selectedRange.endLocation) else { return }

let listIndent = editor.listLineFormatting.indentation

var levelToSet = 0
var prevStyle: NSParagraphStyle?
editor.attributedText.enumerateAttribute(.paragraphStyle, in: listToUpdateRange) { value, range, stop in
levelToSet = 0
if let paraStyle = (value as? NSParagraphStyle)?.mutableParagraphStyle {
let previousLevel = Int(prevStyle?.firstLineHeadIndent ?? 0)/Int(listIndent)
let currentLevel = Int(paraStyle.firstLineHeadIndent)/Int(listIndent)

if currentLevel - previousLevel > 1 {
levelToSet = previousLevel + 1
let indentation = CGFloat(levelToSet) * listIndent
paraStyle.firstLineHeadIndent = indentation
paraStyle.headIndent = indentation
editor.addAttribute(.paragraphStyle, value: paraStyle, at: range)
prevStyle = paraStyle
} else {
prevStyle = value as? NSParagraphStyle
}
}
}
}
}
24 changes: 24 additions & 0 deletions Proton/Sources/Swift/Helpers/NSAttributedString+Range.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,28 @@ public extension NSAttributedString {
let range = NSRange(location: searchTextRange.location, length: searchText.count)
return range
}

func attributedSubstringOrClamped(from range: NSRange) -> NSAttributedString {
let clamped = range.clamped(upperBound: length)
return attributedSubstring(from: clamped)
}

func substringOrClamped(from range: NSRange) -> String {
let clamped = range.clamped(upperBound: length)
return (string as NSString).substring(with: clamped)
}

func attributeOrNil(_ key: NSAttributedString.Key, at location: Int) -> Any? {
return attributesOrEmpty(at: location)[key]
}

func attributesOrEmpty(at location: Int) -> [NSAttributedString.Key: Any] {
guard self.length != 0, location >= 0, location < length else { return [:] }
return attributes(at: location, effectiveRange: nil)
}

func containsAttribute(_ key: NSAttributedString.Key, at location: Int) -> Bool {
attributesOrEmpty(at: location).keys.contains(key)
}

}
4 changes: 0 additions & 4 deletions Proton/Tests/Editor/EditorListsSnapshotTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -594,10 +594,6 @@ class EditorListsSnapshotTests: SnapshotTestCase {

viewController.render(size: CGSize(width: 300, height: 225))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)

// For some reason, a re-render is required in tests
viewController.render(size: CGSize(width: 300, height: 225))
assertSnapshot(matching: viewController.view, as: .image, record: recordMode)
}

func testRendersListWithDifferentAttributeValues() {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.

0 comments on commit 430435f

Please sign in to comment.