pointfreeco / swift-custom-dump

A collection of tools for debugging, diffing, and testing your application's data structures.
MIT License
800 stars 89 forks source link

Why ignore all the attributes of an attributed string? #62

Open tahirmt opened 2 years ago

tahirmt commented 2 years ago

The library offers a default conformance of CustomDumpRepresentable for NSAttributedString that only dumps the string value.

https://github.com/pointfreeco/swift-custom-dump/blob/819d9d370cd721c9d87671e29d947279292e4541/Sources/CustomDump/Conformances/Foundation.swift#L77

This removes all the attributes from the dump That information might be relevant but there's no way to print any additional information. Curious why this was decided.

stephencelis commented 1 year ago

@tahirmt I think just oversight and lack of honing in on a readable format. We'd be down for a custom form at here. Got anything in mind?

tahirmt commented 1 year ago

I implemented CustomDumpStringConvertible for NSAttributedString and while the output works it's not the cleanest. I didn't want to exclude the range but maybe something better could've been done around the range

extension NSAttributedString: CustomDumpStringConvertible {
    public var customDumpDescription: String {
        """
        NSAttributedString(
            string: "\(string)",
            attributes: \(attributesDump)
        )
        """
    }

    private var collectAttributes: [([Key: Any], range: NSRange)] {
        var allAttributes: [([Key: Any], range: NSRange)] = []

        enumerateAttributes(in: NSRange(location: 0, length: length)) { attributes, range, stop in
            allAttributes.append((attributes, range))
        }

        return allAttributes
    }

    private var attributesDump: String {
        var dump = ""
        customDump(collectAttributes, to: &dump, indent: 2)
        return dump
    }
}

extension NSAttributedString.Key: CustomDumpStringConvertible {
    public var customDumpDescription: String {
        if #available(iOS 15.0, *) {
            switch self {
            case .font: return ".font"
            case .paragraphStyle: return ".paragraphStyle"
            case .foregroundColor: return ".foregroundColor"
            case .backgroundColor: return ".backgroundColor"
            case .ligature: return ".ligature"
            case .kern: return ".kern"
            case .tracking: return ".tracking"
            case .strikethroughStyle: return ".strikethroughStyle"
            case .underlineStyle: return ".underlineStyle"
            case .strokeColor: return ".strokeColor"
            case .strokeWidth: return ".strokeWidth"
            case .shadow: return ".shadow"
            case .textEffect: return ".textEffect"
            case .attachment: return ".attachment"
            case .link: return ".link"
            case .baselineOffset: return ".baselineOffset"
            case .underlineColor: return ".underlineColor"
            case .strikethroughColor: return ".strikethroughColor"
            case .obliqueness: return ".obliqueness"
            case .expansion: return ".expansion"
            case .writingDirection: return ".writingDirection"
            case .verticalGlyphForm: return ".verticalGlyphForm"
            case .inlinePresentationIntent: return ".inlinePresentationIntent"
            case .alternateDescription: return ".alternateDescription"
            case .imageURL: return ".imageURL"
            case .languageIdentifier: return ".languageIdentifier"
            case .replacementIndex: return ".replacementIndex"
            case .morphology: return ".morphology"
            case .inflectionRule: return ".inflectionRule"
            case .inflectionAlternative: return ".inflectionAlternative"
            case .presentationIntentAttributeName: return ".presentationIntentAttributeName"
            default:
                return "rawValue(\(rawValue))"
            }
        }
        else {
            switch self {
            case .font: return ".font"
            case .paragraphStyle: return ".paragraphStyle"
            case .foregroundColor: return ".foregroundColor"
            case .backgroundColor: return ".backgroundColor"
            case .ligature: return ".ligature"
            case .kern: return ".kern"
            case .tracking: return ".tracking"
            case .strikethroughStyle: return ".strikethroughStyle"
            case .underlineStyle: return ".underlineStyle"
            case .strokeColor: return ".strokeColor"
            case .strokeWidth: return ".strokeWidth"
            case .shadow: return ".shadow"
            case .textEffect: return ".textEffect"
            case .attachment: return ".attachment"
            case .link: return ".link"
            case .baselineOffset: return ".baselineOffset"
            case .underlineColor: return ".underlineColor"
            case .strikethroughColor: return ".strikethroughColor"
            case .obliqueness: return ".obliqueness"
            case .expansion: return ".expansion"
            case .writingDirection: return ".writingDirection"
            case .verticalGlyphForm: return ".verticalGlyphForm"
            default:
                return "rawValue(\(rawValue))"
            }
        }
    }
}

extension NSLineBreakMode: CustomDumpStringConvertible {
    public var customDumpDescription: String {
        switch self {
        case .byWordWrapping: return ".byWordWrapping"
        case .byCharWrapping: return ".byCharWrapping"
        case .byClipping: return ".byClipping"
        case .byTruncatingHead: return ".byTruncatingHead"
        case .byTruncatingTail: return ".byTruncatingTail"
        case .byTruncatingMiddle: return ".byTruncatingMiddle"
        @unknown default: return "rawValue(\(rawValue)"
        }
    }
}
stephencelis commented 1 year ago

@tahirmt One idea would be to create an XML-like tag representation to unify things. We do a hybrid of this and Markdown in our custom dump representation of SwiftUI text here: https://github.com/pointfreeco/swiftui-navigation/blob/7255c8dd4b3a86a449f6185a2b59b68298c5a6a5/Sources/_SwiftUINavigationState/TextState.swift#L635