0 branches
Tree Top files
Code
Clone with HTTPS:
56 years ago
..

// Copyright 2026 The V Language. All rights reserved. // Use of this source code is governed by an MIT license // that can be found in the LICENSE file.

vlib/x/markdown - Markdown Parser and Renderers

A CommonMark-compliant Markdown parser for V, with HTML and console-friendly plain-text renderers, plus support for GitHub Flavored Markdown (GFM) extensions. Designed for feature parity with github.com/yuin/goldmark.

Features

CommonMark Support

GFM Extensions (via .gfm() helper or individual extensions)

Additional Extensions

Quick Start

Basic Usage

import x.markdown

fn main() {
    html := markdown.to_html('# Hello\n\nWorld')
    text := markdown.to_plaintext('# Hello\n\nWorld')
    println(html)
    println(text)
}

With Extensions

import x.markdown

mut md := markdown.Markdown.new(markdown.Options{
    extensions: markdown.gfm()
})
html := md.convert('| Name |\n|------|\n| Alice |')
println(html) // Renders as HTML table

Fine-Grained Configuration

import x.markdown

fn main() {
    mut md := markdown.Markdown.new(markdown.Options{
        extensions:    [markdown.Extension(markdown.footnote()), markdown.typographer()]
        parser_opts:   markdown.ParserOptions{
            auto_heading_id: true
        }
        renderer_opts: markdown.RendererOptions{
            unsafe_: true
            xhtml:   true
        }
    })
    source := '# Title'
    html := md.convert(source)
    println(html)
}

Parse to AST and Walk

import x.markdown

fn main() {
    mut md := markdown.Markdown.new(markdown.Options{})
    source := '# Hello\n\n`x`'
    doc := md.parse(source)
    doc.walk(fn (node &markdown.Node) bool {
        match node.kind {
            .heading {
                println('Heading level ${node.level}')
            }
            .code_span {
                println('Code: ${node.literal}')
            }
            else {}
        }

        return true
    })
}

API Overview

Top-Level Functions

Main Structs

Markdown

The main processor. Create with Markdown.new(), reuse across multiple calls to share link references.

Methods:

Options (@[params])

pub struct Options {
pub mut:
    extensions    []Extension
    parser_opts   ParserOptions
    renderer_opts RendererOptions
    // Extension feature flags (set by extensions)
    tables          bool
    strikethrough   bool
    linkify         bool
    task_list       bool
    footnotes       bool
    typographer     bool
    definition_list bool
}

ParserOptions (@[params])

pub struct ParserOptions {
pub mut:
    auto_heading_id bool // Generate id from heading text
}

RendererOptions (@[params])

pub struct RendererOptions {
pub mut:
    unsafe_    bool // Allow raw HTML (default: false)
    hard_wraps bool // Convert all \n to <br> (default: false)
    xhtml      bool // Output XHTML self-closing tags (default: false)
}

Node

An AST node. Navigate with .children, inspect with .kind, .literal, .level, etc.

Methods:

Extensions

Available as functions returning extension structs:

Examples

Simple Emphasis

assert markdown.to_html('*em*').contains('<em>em</em>')
assert markdown.to_html('**strong**').contains('<strong>strong</strong>')

Links and Images

// Inline link
html := markdown.to_html('[click](https://example.com)')
// Reference link
html = markdown.to_html('[click][ref]\n\n[ref]: https://example.com')
// Image
html = markdown.to_html('![alt](image.png "title")')

Code Blocks

// Indented code
html := markdown.to_html('    code')
// Fenced code
html = markdown.to_html('```v\nfn main() {}\n```')

Lists

import x.markdown

// Bullet list
html := markdown.to_html('- item 1\n- item 2')
// Ordered list
html = markdown.to_html('1. first\n2. second')
// Task list (enable via extension or task_list option)
html = markdown.to_html('- [x] done', markdown.Options{ task_list: true })

Tables (GFM)

import x.markdown

src := '| Left | Center | Right |\n|:--|:--:|--:|\n| A | B | C |'
html := markdown.to_html(src, markdown.Options{ tables: true })

Footnotes

import x.markdown

src := 'Text[^1]\n\n[^1]: Footnote body.'
html := markdown.to_html(src, markdown.Options{ footnotes: true })
// Renders with <sup> reference and footnote section at bottom

Design Notes

Block Parsing

Inline Parsing

Rendering

Limitations and Known Issues

Testing

Run the test suite:

v -silent test vlib/x/markdown/markdown_test.v

Or write your own:

import x.markdown

fn test_my_markdown() {
    html := markdown.to_html('# Test')
    assert html == '<h1>Test</h1>\n'
}

Contributing

License

MIT, same as V.

References