pointfreeco / swift-custom-dump

A collection of tools for debugging, diffing, and testing your application's data structures.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Why ignore all the attributes of an attributed string?

tahirmt opened this issue · comments

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

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.

@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?

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)"
        }
    }
}

@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