Table cell split on | inside [[url|title]] links (HTML export mangles link + adds a column) #1

Closed
opened 2026-07-02 12:01:53 +00:00 by gffranco · 1 comment
Owner

Summary

The table-row lexer splits a cell on every |, including a | that is part of a link inside the cell. A cell containing a vimwiki link [[url|title]] is therefore split at the link's internal pipe — mangling both the link and the table (a phantom extra column) in the parsed AST, and thus in HTML export (:VimwikiAll2HTML) and any server-driven table formatting.

Where

crates/nuwiki-core/src/syntax/vimwiki/lexer.rs, try_lex_table_row:

// Collect cell boundaries.
let mut sep_positions = Vec::new();
let mut k = i;
while k < end {
    if bytes[k] == b'|' {          // <-- every '|' is a separator
        sep_positions.push(k);
    }
    k += 1;
}

The header-separator detection just above also splits naively (trimmed.split('|')), though that path only matters for |---|---| rows which never contain links, so it's harmless in practice.

Reproduction

Source:

| Name | Link |
| foo  | [[https://example.com|My Site]] |

Expected: 2 columns; cell 2 = the link [[https://example.com|My Site]].
Actual: the row is lexed as 3 cells — foo, [[https://example.com, My Site]] — so the HTML has an extra <td> and the link is broken.

Fix

Make the cell-boundary scan treat | as literal (not a separator) when it is inside:

  • [[ … ]] wikilinks,
  • {{ … }} transclusions,
  • ` … ` inline code,
  • a backslash escape (\|),

matching vimwiki's table cell rules. The client side (this same class of bug in the editor's auto-alignment) was fixed in the plugin repo with a small cell_bar_positions scanner that does exactly this — see gffranco/nuwiki commit 4c187fe (lua/nuwiki/commands.lua / autoload/nuwiki/commands.vim). The server-side lexer wants the equivalent logic when collecting sep_positions.

A good regression case: a table whose cell holds [[a|b]] should lex to a single 2-column row, and its HTML export should contain one <a href="a…">b</a> inside one <td>.

## Summary The table-row lexer splits a cell on **every** `|`, including a `|` that is part of a link inside the cell. A cell containing a vimwiki link `[[url|title]]` is therefore split at the link's internal pipe — mangling both the link and the table (a phantom extra column) in the parsed AST, and thus in HTML export (`:VimwikiAll2HTML`) and any server-driven table formatting. ## Where `crates/nuwiki-core/src/syntax/vimwiki/lexer.rs`, `try_lex_table_row`: ```rust // Collect cell boundaries. let mut sep_positions = Vec::new(); let mut k = i; while k < end { if bytes[k] == b'|' { // <-- every '|' is a separator sep_positions.push(k); } k += 1; } ``` The header-separator detection just above also splits naively (`trimmed.split('|')`), though that path only matters for `|---|---|` rows which never contain links, so it's harmless in practice. ## Reproduction Source: ``` | Name | Link | | foo | [[https://example.com|My Site]] | ``` Expected: 2 columns; cell 2 = the link `[[https://example.com|My Site]]`. Actual: the row is lexed as 3 cells — `foo`, `[[https://example.com`, `My Site]]` — so the HTML has an extra `<td>` and the link is broken. ## Fix Make the cell-boundary scan treat `|` as literal (not a separator) when it is inside: - `[[ … ]]` wikilinks, - `{{ … }}` transclusions, - `` ` … ` `` inline code, - a backslash escape (`\|`), matching vimwiki's table cell rules. The client side (this same class of bug in the editor's auto-alignment) was fixed in the plugin repo with a small `cell_bar_positions` scanner that does exactly this — see `gffranco/nuwiki` commit `4c187fe` (`lua/nuwiki/commands.lua` / `autoload/nuwiki/commands.vim`). The server-side lexer wants the equivalent logic when collecting `sep_positions`. A good regression case: a table whose cell holds `[[a|b]]` should lex to a single 2-column row, and its HTML export should contain one `<a href="a…">b</a>` inside one `<td>`.
Author
Owner

Fixed in 18f1ddf and released in v0.5.1.

The table-row lexer now uses a construct-aware cell_bar_positions scanner that skips | inside [[…]] wikilinks, {{…}} transclusions, and inline code, so a cell holding [[a|b]] lexes to a single 2-column row and exports as one <a href="a…">b</a> inside one <td>. Lexer + HTML-export regression tests were added.

Note: backslash-escaped \| is intentionally left unhandled for now — the inline lexer has no escape handling, so honouring it here would leave a stray backslash in the output. That would need a matching change in lex_inline and can be a follow-up if desired.

Fixed in 18f1ddf and released in v0.5.1. The table-row lexer now uses a construct-aware `cell_bar_positions` scanner that skips `|` inside `[[…]]` wikilinks, `{{…}}` transclusions, and ``…`` inline code, so a cell holding `[[a|b]]` lexes to a single 2-column row and exports as one `<a href="a…">b</a>` inside one `<td>`. Lexer + HTML-export regression tests were added. Note: backslash-escaped `\|` is intentionally left unhandled for now — the inline lexer has no escape handling, so honouring it here would leave a stray backslash in the output. That would need a matching change in `lex_inline` and can be a follow-up if desired.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gffranco/nuwiki-rs#1