Skip to content

feat: add automatic markdown table alignment - #369

Closed
remorses wants to merge 7 commits into
anomalyco:mainfrom
remorses:format-table
Closed

remorses wants to merge 7 commits into
anomalyco:mainfrom
remorses:format-table

Conversation

@remorses

@remorses remorses commented Nov 30, 2025 •

Copy link
Copy Markdown
Contributor

Automatically formats markdown tables by padding columns to align content when rendering. Using tree sitter AST

@remorses
remorses force-pushed the format-table branch 4 times, most recently from d4396a7 to 071da45 Compare November 30, 2025 18:01
- Format markdown tables by padding columns to align content
- Use tree-sitter markdown_inline parser to detect concealed characters (backticks, bold **, italic *)
- Return transformedContent from highlightOnce for formatted markdown
- Add React example demonstrating table formatting with copy-to-clipboard
- Add comprehensive tests for table formatting
@remorses
remorses marked this pull request as ready for review December 18, 2025 23:48
Copilot AI review requested due to automatic review settings December 18, 2025 23:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR adds automatic markdown table alignment functionality that formats table columns by padding cells to align content when rendering. The feature uses Tree-sitter AST parsing to identify tables and calculate display widths that account for concealed markdown formatting characters (backticks, bold/italic markers).

Key Changes:

  • Implemented table formatting logic that calculates visual display width (excluding concealed characters) and pads cells accordingly
  • Extended the highlighting pipeline to support transformed content that can be different from the original input
  • Added comprehensive test coverage for various table formatting scenarios including inline code, bold/italic text, and alignment markers

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/react/examples/markdown-table.tsx Adds a demo application showcasing automatic table alignment with syntax highlighting and clipboard copying functionality
packages/core/src/renderables/Code.ts Integrates transformed content support to display formatted tables instead of original markdown
packages/core/src/lib/tree-sitter/parser.worker.ts Implements core table formatting logic including display width calculation and column alignment with Tree-sitter AST traversal
packages/core/src/lib/tree-sitter/client.ts Updates type definitions to support transformedContent in the highlighting response
packages/core/src/lib/tree-sitter/client.test.ts Adds comprehensive test suite covering various table formatting scenarios and edge cases

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- use tree-sitter AST nodes (pipe_table_cell) instead of string splitting
- add conceal parameter to control width calculation for formatting markers
- use Bun.stringWidth for proper unicode/emoji terminal width
- preserve trailing whitespace after tables
- pass conceal setting from CodeRenderable to highlightOnce
@remorses

Copy link
Copy Markdown
Contributor Author

Here is how table rendering looks like now. Formatting is handled both in conceal and non conceal mode using treesitter AST

I added a lot of inline snapshots so you can see how the result looks like. There is a small delay because treesitter parsing runs in the background asynchronously

Screenshot 2025-12-19 at 01 35 58

remorses

This comment was marked as off-topic.

@kommander

Copy link
Copy Markdown
Collaborator

You'll probably hate me for this 😬, but I don't like that the formatting happens in the parser worker, that it is type aware and that it re-parses after formatting. I think the formatting should just happen before the markdown source is handed to the parser. I could see it as CodeRenderable transform prop, were the transform method gets the type and content and can format it before it is set as content and before it is handed to the parser.

@kommander

Copy link
Copy Markdown
Collaborator

Markdown parsing perspectively: I don't want to use this kind of markdown parsing in opencode long term. I want to have a StreamingMarkdownRenderable that used default components for blocks that can be overridden with custom components, so it would also use a proper TableRenderable.

The flow would be kind of:

  • hold a raw markdown buffer
  • take markdown chunks
  • detect a block
    • if full block render that
    • use CodeRenderable for fenced code blocks and TableRenderable for table etc.
  • shift and write rest that is not a detected block yet to raw markdown buffer

That will allow showing fenced code blocks for example with custom background/borders/whatever and it does not have to re-parse the full markdown for every update it gets, only the last block worst case.

@franlol

franlol commented Dec 19, 2025

Copy link
Copy Markdown

I previously implemented this same solution at Opentui level, using Tree-sitter, and it produced really good results:

Screenshot_2025-12-02_at_14 56 07

However, I ultimately had to delete the PR because it felt more like a hack than a true solution. Also, this is parsing the text on each render.

Instead, I moved to a more robust approach and developed a parser at the plugin level that parses the text only once once the text.complete is triggered: OpenCode MD Table Formatter

@remorses remorses closed this Dec 19, 2025
@remorses

remorses commented Dec 19, 2025 •

Copy link
Copy Markdown
Contributor Author

Would you implement the markdown renderable in Zig or TypeScript?

I thought about using marked npm package which exposes a fast lexer to find top level AST nodes, then I would use marked parser to render the nodes via opentui renderables

Tables could even use CSS grid when it is in Yoga so that they render well even if there is no space horizontally by wrapping the text

I don't like much the plugin @franlol because it uses regex to parse the markdown, which probably have unknown bugs and edge cases

@kommander

Copy link
Copy Markdown
Collaborator

@remorses For now it is only possible to do that in typescript. Using the marked package seems fine, as long as it does not re-parse the whole text for every incoming chunk in the stream, but incremental.

I think something like @franlol did works well, as markdown is a really simple, small and very well defined set. Simple compared to html or a programming language. Not that much that can go wrong. We should have that formatting by default in opencode. Can we add it as a default plugin maybe?

@remorses

Copy link
Copy Markdown
Contributor Author

Markdown parsing is more complex than it looks trust me, using marked is totally worth it

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.

4 participants