@@ -26,6 +26,47 @@ extension TextLayoutManager {
2626 // MARK: - Layout Lines
2727
2828 /// Lays out all visible lines
29+ ///
30+ /// ## Overview Of The Layout Routine
31+ ///
32+ /// The basic premise of this method is that it loops over all lines in the given rect (defaults to the visible
33+ /// rect), checks if the line needs a layout calculation, and performs layout on the line if it does.
34+ ///
35+ /// The thing that makes this layout method so fast is the second point, checking if a line needs layout. To
36+ /// determine if a line needs a layout pass, the layout manager can check three things:
37+ /// - **1** Was the line laid out under the assumption of a different maximum layout width?
38+ /// Eg: If wrapping is toggled, and a line was initially long but now needs to be broken, this triggers that
39+ /// layout pass.
40+ /// - **2** Was the line previously not visible? This is determined by keeping a set of visible line IDs. If the
41+ /// line does not appear in that set, we can assume it was previously off screen and may need layout.
42+ /// - **3** Was the line entirely laid out? We break up lines into line fragments. When we do layout, we determine
43+ /// all line fragments but don't necessarily place them all in the view. This checks if all line fragments have
44+ /// been placed in the view. If not, we need to place them.
45+ ///
46+ /// Once it has been determined that a line needs layout, we perform layout by recalculating it's line fragments,
47+ /// removing all old line fragment views, and creating new ones for the line.
48+ ///
49+ /// ## Laziness
50+ ///
51+ /// At the end of the layout pass, we clean up any old lines by updating the set of visible line IDs and fragment
52+ /// IDs. Any IDs that no longer appear in those sets are removed to save resources. This facilitates the text view's
53+ /// ability to only render text that is visible and saves tons of resources (similar to the lazy loading of
54+ /// collection or table views).
55+ ///
56+ /// The other important lazy attribute is the line iteration. Line iteration is done lazily. As we iterate
57+ /// through lines and potentially update their heights, the next line is only queried for *after* the updates are
58+ /// finished.
59+ ///
60+ /// ## Reentry
61+ ///
62+ /// An important thing to note is that this method cannot be reentered. If a layout pass is begun while a layout
63+ /// pass is already ongoing, internal data structures will be broken. In debug builds, this is checked with a simple
64+ /// boolean and assertion.
65+ ///
66+ /// To help ensure this property, all view modifications are done in a `CATransaction`. This ensures that only after
67+ /// we're done inserting and removing line fragment views, does macOS call `layout` on any related views. Otherwise,
68+ /// we may cause a layout pass when a line fragment view is inserted and cause a reentrance in this method.
69+ ///
2970 /// - Warning: This is probably not what you're looking for. If you need to invalidate layout, or update lines, this
3071 /// is not the way to do so. This should only be called when macOS performs layout.
3172 public func layoutLines( in rect: NSRect ? = nil ) { // swiftlint:disable:this function_body_length
0 commit comments