Skip to content

Detect grid tables and emit error on parse#159

Open
rundel wants to merge 1 commit intoquarto-dev:mainfrom
rundel:feature/grid-table-error
Open

Detect grid tables and emit error on parse#159
rundel wants to merge 1 commit intoquarto-dev:mainfrom
rundel:feature/grid-table-error

Conversation

@rundel
Copy link
Copy Markdown

@rundel rundel commented May 5, 2026

Based on the discussion in #156

Very much a proof of concept / vibe coded state at the moment but was able to handle the basic test cases I threw at it.

Sample output:

Fixture 1: bare grid tables (no block quote)

gt_block_quote1.qmd:

+----+
| oh |
+----+

+------+--------+
| key  | val    |
+------+--------+
| a    | hello  |
+------+--------+

+-------------+
| ```bash     |
| ls -la      |
| ```         |
+-------------+

```
+----+
| oh |
+----+
```

Run:

cargo run --bin q2 -- render gt_block_quote/gt_block_quote1.qmd

Output:

Error: [Q-2-36] Grid tables are not supported
   ╭─[ gt_block_quote1.qmd:1:1 ]
   │
 1 │ ╭─▶ +----+
 2 │ │   | oh |
 3 │ ├─▶ +----+
   │ │
   │ ╰──────────── Grid tables aren't supported. Use a pipe table instead.
───╯

Error: [Q-2-36] Grid tables are not supported
   ╭─[ gt_block_quote1.qmd:5:1 ]
   │
 5 │ ╭─▶ +------+--------+
 6 │ │   | key  | val    |
 7 │ │   +------+--------+
 8 │ │   | a    | hello  |
 9 │ ├─▶ +------+--------+
   │ │
   │ ╰─────────────────────── Grid tables aren't supported. Use a pipe table instead.
───╯

Error: [Q-2-36] Grid tables are not supported
    ╭─[ gt_block_quote1.qmd:11:1 ]
    │
 11 │ ╭─▶ +-------------+
 12 │ │   | ```bash     |
 13 │ │   | ls -la      |
 14 │ │   | ```         |
 15 │ ├─▶ +-------------+
    │ │
    │ ╰───────────────────── Grid tables aren't supported. Use a pipe table instead.
────╯

(Note: the trailing fenced code block in the source — lines ~17–21 — is not flagged. Content inside ``` fences is treated as a code block, so +----+ / | oh | lines there don't trigger the grid-table detector. This is the correct behavior.)


Fixture 2: grid table inside a single block quote

gt_block_quote2.qmd:

> +----+
> | oh |
> +----+

Run:

cargo run --bin q2 -- render gt_block_quote/gt_block_quote2.qmd

Output:

Error: [Q-2-36] Grid tables are not supported
   ╭─[ gt_block_quote2.qmd:1:3 ]
   │
 1 │ ╭─▶ > +----+
 2 │ │   > | oh |
 3 │ ├─▶ > +----+
   │ │
   │ ╰────────────── Grid tables aren't supported. Use a pipe table instead.
───╯

Fixture 3: nested block quotes with intermediate non-grid lines

gt_block_quote3.qmd:

> > +----+
> > | oh |
> > +----+
> | pipe-table-now |
> |-|
> | no |
> > +----+
> > | no |
> > +----+

Run:

cargo run --bin q2 -- render gt_block_quote/gt_block_quote3.qmd

Output:

Error: [Q-2-36] Grid tables are not supported
   ╭─[ gt_block_quote3.qmd:1:5 ]
   │
 1 │ ╭─▶ > > +----+
 2 │ │   > > | oh |
 3 │ ├─▶ > > +----+
   │ │
   │ ╰──────────────── Grid tables aren't supported. Use a pipe table instead.
───╯

Error: [Q-2-36] Grid tables are not supported
   ╭─[ gt_block_quote3.qmd:7:5 ]
   │
 7 │ ╭─▶ > > +----+
 8 │ │   > > | no |
 9 │ ├─▶ > > +----+
   │ │
   │ ╰──────────────── Grid tables aren't supported. Use a pipe table instead.
───╯

Quarto Markdown intentionally doesn't support Pandoc-style grid
tables. Today they fall through as paragraphs (silently mis-parsed)
or generic ERROR nodes — no diagnostic that names the construct or
captures its text.

tree-sitter-qmd
  - Add a GRID_TABLE external token to scanner.c. parse_plus is
    refactored to disambiguate after the leading `+`: `+-`, `+=`, or
    `++` enters parse_grid_table_after_first_plus(); space/tab/EOL
    keeps the existing list-marker path.
  - parse_grid_table_after_first_plus greedily consumes a `+...+`
    border followed by one or more `|...|` body / border lines,
    walking nested block-quote prefixes via the open_blocks stack
    so grids inside `>` / `> >` are captured correctly.
  - grammar.js wires `grid_table` into _block_not_section.
  - 5 new corpus tests in test/corpus/grid_table.txt cover plain,
    single-quoted, double-nested-with-intermediate-paragraph,
    lone-border (negative), and `+====+` separator forms.

pampa
  - native_visitor in pandoc/treesitter.rs handles `grid_table`:
    builds a Q-2-36 DiagnosticMessage and falls back to a
    `Block::RawBlock { format: "qmd-grid-table" }` so the AST stays
    well-formed.
  - The diagnostic uses a layered Ariadne rendering:
      1. multi-line main label spans the whole table (provides
         `╭─▶ … ╰─` corners and the red highlight on table content);
      2. empty-content `add_detail_at` labels on interior body lines
         force Ariadne to display them instead of eliding to `┆`;
      3. `add_faded_at` labels on `>` block-quote prefixes after the
         opening border render those columns in Ariadne's grey
         (matching the prefix on line 1, which falls outside every
         label).
  - 5 new integration tests in tests/test_grid_table_error.rs.

quarto-error-reporting
  - New `DetailKind::Faded` variant + `add_faded_at` builder method.
    Maps to Ariadne's `unimportant_color` via a named constant
    ARIADNE_UNIMPORTANT_COLOR (mirror of ariadne 0.6.0's private
    `Config::unimportant_color`, currently `Color::Fixed(249)`).
  - Two generic improvements that this feature needed:
      - Skip `Label::with_message` when detail content is empty so
        empty labels don't draw a \`╰── \` arrow row in the snippet.
      - Set `Label::with_order(end_offset)` on every label so labels
        sort by line and stay in one Ariadne group instead of
        producing duplicated snippet blocks.
  - error_catalog.json registers Q-2-36 ("Grid tables are not
    supported"). quarto-lsp-core::DetailKind folds Faded into Note.

Side effect — regenerating parser.c shifts LR state numbers, so
crates/pampa/resources/error-corpus/_autogen-table.json was
regenerated via crates/pampa/scripts/build_error_table.ts (the
documented Merr workflow in crates/pampa/CLAUDE.md). Skipping this
step silently breaks every autogen-table-driven diagnostic
including Q-2-10.

Verification
  - tree-sitter test: 475/475 corpus
  - cargo nextest run --workspace: 8390/8390
  - cargo xtask verify --skip-hub-build --skip-hub-tests: clean
  - cargo xtask lint: 677 files clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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