Skip to content

feat(input): block-editing pipeline foundation#460

Open
wildseansy wants to merge 2 commits into
software-mansion:mainfrom
wildseansy:feat/block-pipeline
Open

feat(input): block-editing pipeline foundation#460
wildseansy wants to merge 2 commits into
software-mansion:mainfrom
wildseansy:feat/block-pipeline

Conversation

@wildseansy

Copy link
Copy Markdown

What

First PR in the block-editing series requested in #455 (closed in favor of this rebuild). This adds the generic block-editing pipeline foundation for the editable input, mirroring the existing inline-style pipeline (StyleType → FormattingStore → StyleHandler → InputFormatter → serializer/parser). No concrete block type is registered yet — with no handler the editor behaves exactly as today (plain paragraphs, all inline styles unaffected). A follow-up PR plugs the first handler (headings, H1–H6) in without touching the orchestrator.

Abstractions (both platforms)

Inline (existing) Block (this PR)
ENRMInputStyleType / StyleType ENRMInputBlockType / BlockType (Paragraph default)
ENRMStyleHandler / StyleHandler ENRMBlockHandler / BlockHandler — paragraph attributes, markdown line prefix, parser-block matching
ENRMFormattingStore / FormattingStore ENRMBlockStore / BlockStore — generic, line-scoped block ranges + adjustForEdit
formatter style-handler registry formatter block-handler registry (empty here) + applyBlockRanges
serializer inline delimiters block handlers contribute line prefixes (no central switch)
parser inline span mapping iOS: real md4c enter_block/leave_block (replaces the no-op TODO); Android: block ranges from the shared AST (C++ untouched)

Notes addressing the #455 review

  • No monolithic orchestrator logic — blocks plug in via handlers, like bold did for inline.
  • iOS parser uses md4c enter_block/leave_block (the existing TODO is removed), not a post-pass.
  • Android reuses the shared MarkdownASTNode the readonly renderer already emits — MD4CParser.cpp is unchanged, so readonly parsing is unaffected.
  • No silent serializer fallback — the block-aware serializer asserts the line-count invariant instead of silently dropping blocks.

Testing

Both platforms build green (example app, iOS sim + Android emulator). Behavior is inert (byte-for-byte unchanged) with no block handler registered.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EhuNE6PjRGjroxobhdd72q

wildseansy and others added 2 commits June 28, 2026 18:44
Add the generic block-editing abstractions for the input component,
mirroring the inline-style pipeline (StyleType→FormattingStore→StyleHandler
→InputFormatter→serializer/parser). No concrete block type is registered
yet, so behavior is unchanged (plain paragraphs, inline styles intact); a
later change plugs a heading handler in without touching the orchestrator.

- ENRMInputBlockType enum + block paragraph attribute keys
- ENRMBlockRange model (type + range + generic level payload)
- ENRMBlockHandler protocol: paragraph attributes, markdown line prefix,
  md4c block matching — sufficient for headings and list items
- ENRMBlockStore: generic line-scoped block range store with edit adjustment
- InputFormatter: block-handler registry (empty) + applyBlockRanges
- Serializer: block-aware overload; asserts the line-count invariant
  instead of silently dropping blocks
- Parser: real md4c enter_block/leave_block via a kSupportedBlocks table
  (paragraph-only today), replacing the no-op TODO
- Orchestrator: wires a block store through apply/edit/import/export, inert
  until a handler is registered

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhuNE6PjRGjroxobhdd72q
Android mirror of the iOS block-editing foundation: the generic block
abstractions for the input component, paralleling the inline-style pipeline
(StyleType→FormattingStore→StyleHandler→InputFormatter→serializer/parser).
No concrete block type is registered, so behavior is unchanged; a later
change plugs a heading handler in without touching the orchestrator.

- BlockType enum (PARAGRAPH default) + BlockRange model (type/start/end/level)
- BlockHandler interface: createSpans, spanClasses, markdownLinePrefix,
  matchesNodeType — sufficient for headings and list items
- BlockStore: generic line-scoped block range store with edit adjustment
  (reuses FormattingStore's edit-overlap logic)
- InputFormatter: block-handler registry (empty) + applyBlockFormatting
- MarkdownSerializer: block-aware overload; asserts the line-count invariant
  instead of silently dropping blocks
- InputParser: ParseResult.blockRanges from the shared AST (paragraph-only
  today; the C++ parser is unchanged so readonly rendering is unaffected)
- Orchestrator + event emitter: wire a block store through apply/edit/
  import/export, inert until a handler is registered

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhuNE6PjRGjroxobhdd72q
@wildseansy

Copy link
Copy Markdown
Author

To show how this foundation is meant to be used, here's the first handler (headings, H1–H6) plugging into it as a reference PR, stacked on this branch so the diff is only the heading delta:

wildseansy#1

It demonstrates the intended extension pattern — a new block type is added by implementing the BlockHandler protocol (paragraph styling + markdown line prefix + parser-block matching) and registering it, with no orchestrator changes. Per-level heading sizes are configured via markdownStyle.h1..h6, mirroring the readonly renderer. Verified on both platforms (clean editor text, per-level sizing, single-marker round-trip). Lists would follow the same pattern.

@wildseansy

wildseansy commented Jun 29, 2026

Copy link
Copy Markdown
Author

@hryhoriiK97 - base pipeline ready for review. See wildseansy#1 for how it's used for headings, and wildseansy#2 for the headings fully integrated into the test app

@hryhoriiK97 hryhoriiK97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wildseansy thanks for the PR! I'll try to take a look this week if possible. We have a lot on our plate right now, so I can't make any promises.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants