Skip to content

Fix schema_like? to recognize markdown-formatted annotation rows#354

Open
nashirox wants to merge 1 commit into
drwl:mainfrom
nashirox:fix/schema-like-markdown-rows
Open

Fix schema_like? to recognize markdown-formatted annotation rows#354
nashirox wants to merge 1 commit into
drwl:mainfrom
nashirox:fix/schema-like-markdown-rows

Conversation

@nashirox

@nashirox nashirox commented Jul 9, 2026

Copy link
Copy Markdown

Problem

Since v4.23.0, re-running annotaterb with format_markdown: true on a file that is already correctly annotated duplicates the entire schema annotation block instead of leaving the file unchanged / replacing it in place.

Example (Worker < User, STI model, format_markdown: true):

# ## Schema Information
#
# Table name: `users`
#
# ### Columns
#
# Name       | Type            | Attributes
# ---------- | --------------- | ---------------------------
# **`id`**   | `integer`       | `not null, primary key`
#
# ### Columns          <- duplicated block starts here
#
# Name       | Type            | Attributes
# ---------- | --------------- | ---------------------------
# **`id`**   | `integer`       | `not null, primary key`
#

require 'rails_helper'

Running annotaterb again on the already-duplicated file duplicates it a third time, and so on - it's not idempotent.

Root cause

AnnotationFinder#schema_like? (added in #334) decides where an existing annotation block ends by walking forward from its start and checking whether each subsequent line "looks like" part of the schema:

comment.match?(/\A#\s{2,}\S/)

This assumes at least two leading spaces after #, which holds for the default/plain format (# id :integer not null) but not for format_markdown: true. Markdown-formatted section headers, table rows, and list items are all rendered with a single leading space after #:

  • # ### Columns (section header)
  • # **`id`** | `integer` | ... (table row)
  • # * `index_name`: (foreign key/index/enum list item)

Because none of these match the two-space heuristic (and SCHEMA_HEADER_EXACT only matches the plain header text, not the ### -prefixed markdown version), walk_forward_through_schema stops right after the very first line of the table (typically the header row). annotation_end is therefore computed far too early.

When SingleFileAnnotator re-runs on the file, AnnotatedFile::Updater#update only replaces this truncated region:

@file_content.sub(@parsed_file.annotations) { new_annotation }

...leaving the rest of the old annotation (the actual column/index/foreign key rows) sitting in the file, immediately followed by the freshly generated one. Every subsequent run adds another copy.

This also affects EnumAnnotation (# Enums) in the default format, since EnumAnnotation::Annotation::HEADER_TEXT was never added to SCHEMA_HEADER_EXACT — likely an oversight from the same PR that introduced show_enums.

Fix

  • Added EnumAnnotation::Annotation::HEADER_TEXT ("Enums") to SCHEMA_HEADER_EXACT, since it was missing alongside Indexes/Foreign Keys/Check Constraints.
  • Added markdown-aware detection to schema_like?:
    • ### <name> section headers, matched against known section names with the ### prefix stripped (Columns has no HEADER_TEXT constant since it's inlined in Annotation::MarkdownHeader, so it's listed explicitly).
    • Pipe-delimited table rows, requiring two or more | separators (not just one) so that a one-off user comment containing a stray | isn't mistaken for a table row.
    • Bulleted list items for foreign keys/indexes/enums (* `name`: / * **`col`**), requiring a backtick right after the bullet marker so an unrelated comment starting with * isn't swallowed.

Tests

  • annotation_finder_spec.rb: boundary detection for a markdown-formatted annotation, plus two adversarial cases confirming a trailing user comment containing a single | or a plain * bullet is not absorbed into the annotation.
  • annotating_a_file_with_comments_spec.rb: end-to-end regression — running SingleFileAnnotator with format_markdown: true on an already-annotated file leaves it unchanged (previously it duplicated the block).

All existing specs pass (851 examples, 0 failures outside of spec/integration, which requires a full dummy Rails app/DB setup not available in this environment — verified those failures are identical with and without this change).

Possible follow-up (not included in this PR)

schema_like? currently infers whether the file is markdown-formatted from the shape of each line (spacing, ###, |, bullets). Since the caller (annotaterb itself) already knows the configured format_markdown option at
this point, an alternative design would be to pass that flag into AnnotationFinder and branch explicitly in schema_like?, rather than re-deriving it heuristically. That would avoid needing to keep schema_like? in sync with Annotation::MarkdownHeader / ColumnComponent#to_markdown / etc. every time the markdown output format
changes. Happy to follow up with that refactor in a separate PR if maintainers are interested — keeping this PR focused on the bug fix so it's easy to review and merge quickly.

@nashirox nashirox force-pushed the fix/schema-like-markdown-rows branch from 422cc06 to d4c4d2b Compare July 9, 2026 06:07
@nashirox nashirox changed the title Fix schema_like? to recognize markdown-formatted annotation rows (regression from #339 / v4.23.0) Fix schema_like? to recognize markdown-formatted annotation rows Jul 9, 2026
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.

1 participant