Skip to content

StructuredText crashes on large markdown documents (~200+ blocks) #23

Description

@bertranddour

Description

StructuredText becomes unresponsive and eventually crashes (EXC_BAD_ACCESS) when rendering large markdown documents (tested with a 2,537-line / 98KB document with ~200+ blocks). The console floods with:

onChange(of: AnyTextLayoutCollection) action tried to update multiple times per frame.
Geometry action is cycling between duplicate values.

Edit mode using native UITextView/NSTextView is unaffected — only StructuredText view rendering triggers this.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

StructuredText should render large markdown documents without freezing or crashing, similar to how native text views handle the same content.

Actual behavior

The view freezes during initial render, floods the console with layout cycle warnings, and eventually crashes with EXC_BAD_ACCESS. Disabling .textSelection(.enabled) delays but doesn't prevent the freeze.

Steps to reproduce

ScrollView {
    StructuredText(markdown: veryLargeMarkdown)  // ~2500 lines, ~200 blocks
        .textual.textSelection(.enabled)
}

I've traced the root cause to two interacting issues:

1. Eager block rendering (BlockContent.swift:13-22)BlockContent uses a non-lazy ForEach inside BlockVStack, instantiating all blocks at once. BlockVStackLayout calls sizeThatFits() / placeSubviews() on every subview, which is fundamentally incompatible with lazy rendering.

2. Layout change cascade (TextSelectionInteraction.swift:26-31)overlayTextLayoutCollection creates a new LiveTextLayoutCollection on every geometry change. With hundreds of blocks resolving layout simultaneously, onChange fires repeatedly per frame, and each setLayoutCollection call triggers state mutations causing further relayout — a runaway feedback loop.

Note: PR #14 addressed related layout-cycle warnings for tables. This is the same class of issue at larger scale.

Textual version information

0.3.1 (commit 5b06b81)

Destination operating system

iOS 26 / macOS 26

Xcode version information

Version 26.2 (17C52)

Swift Compiler version information

Apple Swift version 6.2.3 (swiftlang-6.2.3.3.21 clang-1700.6.3.2)                                                
Target: arm64-apple-macosx26.0                                                                                   
swift-driver version: 1.127.14.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions