func layoutManager()

in Sources/TwitterTextEditor/LayoutManager.swift [263:321]


    func layoutManager(_ layoutManager: NSLayoutManager,
                       didCompleteLayoutFor textContainer: NSTextContainer?,
                       atEnd layoutFinishedFlag: Bool)
    {
        log(type: .debug, "text container: %@, at end: %@", String(describing: textContainer), String(describing: layoutFinishedFlag))

        // TODO: Verify if it's right timing to release cache.
        glyphsCache = []

        // This is a timing to lay out views in attachments.
        guard let textStorage = layoutManager.textStorage,
              let textContainer = textContainer
        else {
            return
        }

        let glyphsToShow = layoutManager.glyphRange(for: textContainer)

        // Following logic is same as the one in LayoutManager for `drawGlyphs(forGlyphRange:at)`.

        let count = glyphsToShow.length
        var properties = [NSLayoutManager.GlyphProperty](repeating: [], count: count)
        var characterIndexes = [Int](repeating: 0, count: count)
        properties.withUnsafeMutableBufferPointer { props -> Void in
            characterIndexes.withUnsafeMutableBufferPointer { charIndexes -> Void  in
                layoutManager.getGlyphs(in: glyphsToShow,
                                        glyphs: nil,
                                        properties: props.baseAddress,
                                        characterIndexes: charIndexes.baseAddress,
                                        bidiLevels: nil)
            }
        }

        // TODO: Measure performance and consider different approach.
        // This scans entire glyph range once.

        let signpostScanGlyphsToShow = signpost(name: "Scan glyphs to show", "length: %d", glyphsToShow.length)
        signpostScanGlyphsToShow.begin()
        for index in 0..<glyphsToShow.length where properties[index].contains(.controlCharacter) {
            let attributes = textStorage.attributes(at: characterIndexes[index], effectiveRange: nil)
            if let suffixedAttachment = attributes[.suffixedAttachment] as? TextAttributes.SuffixedAttachment,
               case .view(let view, let layoutInTextContainer) = suffixedAttachment.attachment
            {
                let glyphIndex = glyphsToShow.location + index
                let lineFragmentOrigin = layoutManager.lineFragmentRect(forGlyphAt: glyphIndex,
                                                                        effectiveRange: nil,
                                                                        withoutAdditionalLayout: true).origin
                let locationInLineFragment = layoutManager.location(forGlyphAt: glyphIndex)

                let locationInContext = CGPoint(
                    x: lineFragmentOrigin.x + locationInLineFragment.x,
                    y: lineFragmentOrigin.y
                )
                let frame = CGRect(origin: locationInContext, size: suffixedAttachment.size)
                layoutInTextContainer(view, frame)
            }
        }
        signpostScanGlyphsToShow.end()
    }