pax_global_header 0000666 0000000 0000000 00000000064 15177100052 0014510 g ustar 00root root 0000000 0000000 52 comment=36c5f547144df2d01970a5792d68c71a3380b227 markdown-it-py-4.2.0/ 0000775 0000000 0000000 00000000000 15177100052 0014375 5 ustar 00root root 0000000 0000000 markdown-it-py-4.2.0/.github/ 0000775 0000000 0000000 00000000000 15177100052 0015735 5 ustar 00root root 0000000 0000000 markdown-it-py-4.2.0/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15177100052 0020120 5 ustar 00root root 0000000 0000000 markdown-it-py-4.2.0/.github/ISSUE_TEMPLATE/bug_report.yml 0000664 0000000 0000000 00000003205 15177100052 0023013 0 ustar 00root root 0000000 0000000 name: Report a problem 🐛 description: Problem reports are for when something behaves incorrectly, or differently from how you'd expect. labels: [bug] body: - type: textarea id: describe attributes: label: Describe the bug description: | Provide a short description (one or two sentences) about the problem. What did you expect to happen, and what is actually happening? If possible, provide screenshots or error messages that you've encountered. value: | **context** When I do ___. **expectation** I expected ___ to occur. **bug** But instead ___ happens Here's an error message I ran into... **problem** This is a problem for people doing ___ because ___. validations: required: true - type: textarea id: reproduce attributes: label: Reproduce the bug description: | Provide information that others may use to re-produce this behavior. For example: - Step-by-step instructions that others can follow. - Links to a website that demonstrates the bug. - Information about certain conditions that the bug pops up. placeholder: | 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error validations: required: true - type: textarea id: environment attributes: label: List your environment description: | List the environment needed to reproduce the error. Here are a few ideas: - Version of markdown-it-py - Versions of mdit-py-plugins - The version of Python you're using. - Your operating system validations: required: false markdown-it-py-4.2.0/.github/ISSUE_TEMPLATE/config.yml 0000664 0000000 0000000 00000000350 15177100052 0022106 0 ustar 00root root 0000000 0000000 contact_links: - name: Questions or general discussion ❓🗣️ url: https://github.com/executablebooks/markdown-it-py/discussions about: Use Disussions for general conversations that aren't meant for actionable Issues. markdown-it-py-4.2.0/.github/ISSUE_TEMPLATE/enhancement.yml 0000664 0000000 0000000 00000002200 15177100052 0023122 0 ustar 00root root 0000000 0000000 name: Request an enhancement 💡 description: Suggest an idea for this project labels: [enhancement] body: - type: textarea id: context attributes: label: Context description: | - Provide background to help others understand this issue. - Describe the problem or need you'd like to address. validations: required: true - type: textarea id: proposal attributes: label: Proposal description: | - A simple and clear description of what you're proposing. - Ideas or constraints for how to implement this proposal - Important considerations to think about or discuss validations: required: false - type: textarea id: tasks attributes: label: Tasks and updates description: | Use this area to track ongoing work and to-do items. The more specific the better. _If you can't think of anything then just leave this blank and we can fill it in later! This can be filled in as we understand more about an issue._ placeholder: | - [ ] Discuss and decide on what to do... - [ ] Implement partial feature A... validations: required: false markdown-it-py-4.2.0/.github/dependabot.yml 0000664 0000000 0000000 00000001126 15177100052 0020565 0 ustar 00root root 0000000 0000000 # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: github-actions directory: / commit-message: prefix: ⬆️ schedule: interval: weekly - package-ecosystem: pip directory: / commit-message: prefix: ⬆️ schedule: interval: weekly markdown-it-py-4.2.0/.github/workflows/ 0000775 0000000 0000000 00000000000 15177100052 0017772 5 ustar 00root root 0000000 0000000 markdown-it-py-4.2.0/.github/workflows/benchmark.yml 0000664 0000000 0000000 00000002222 15177100052 0022445 0 ustar 00root root 0000000 0000000 name: benchmark on: push: branches: [master] jobs: benchmark-packages: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.10" - name: install pandoc uses: r-lib/actions/setup-pandoc@v2 with: pandoc-version: '2.6' - name: Install tox run: | python -m pip install --upgrade pip pip install tox - name: Run package benchmarks run: tox -e py310-bench-packages -- --benchmark-min-rounds 20 --benchmark-json bench-packages.json # - name: Upload package data # uses: actions/upload-artifact@v3 # with: # name: bench-packages # path: bench-packages.json # if-no-files-found: error - name: Store benchmark result uses: aiidateam/github-action-benchmark@v3 with: name: Parsing Benchmarks output-file-path: bench-packages.json github-token: ${{ secrets.GITHUB_TOKEN }} auto-push: true commit-msg-append: "[ci skip]" one-chart-groups: packages,plugins fail-on-alert: false markdown-it-py-4.2.0/.github/workflows/copilot-setup-steps.yml 0000664 0000000 0000000 00000001303 15177100052 0024455 0 ustar 00root root 0000000 0000000 name: Copilot Setup Steps on: workflow_dispatch: push: paths: - .github/workflows/copilot-setup-steps.yml pull_request: paths: - .github/workflows/copilot-setup-steps.yml jobs: copilot-setup-steps: runs-on: ubuntu-latest permissions: contents: read steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.11" cache: pip - name: Install uv run: pip install uv - name: Install pre-commit and pre-commit-uv run: pip install pre-commit pre-commit-uv - name: Install tox and tox-uv run: pip install tox tox-uv markdown-it-py-4.2.0/.github/workflows/fuzz.yml 0000664 0000000 0000000 00000002230 15177100052 0021510 0 ustar 00root root 0000000 0000000 name: fuzzing # This action runs fuzzing for a brief period of time, # only aginst the actual code added in the PR. # It is intended a relatively quick check, # to guard against code introducing crashes in the Markdown parsing, # which should in principle always run against any text input. # See: https://google.github.io/oss-fuzz/getting-started/continuous-integration/#how-it-works # Note, to reproduce a crash locally, copy to `testcase` file` and run: `tox -e fuzz` on: pull_request: paths-ignore: ['docs/**', 'tests/**'] jobs: Fuzzing: runs-on: ubuntu-latest steps: - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'markdown-it-py' language: python - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'markdown-it-py' language: python fuzz-seconds: 60 - name: Upload Crash uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts markdown-it-py-4.2.0/.github/workflows/tests.yml 0000664 0000000 0000000 00000007160 15177100052 0021663 0 ustar 00root root 0000000 0000000 # This workflow will install Python dependencies, run tests and lint with a variety of Python versions # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions name: continuous-integration on: push: branches: [master] tags: - "v[0-9]+.[0-9]+.[0-9]+*" pull_request: schedule: - cron: '0 0 * * 0' # every week jobs: pre-commit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.10' - uses: pre-commit/action@v3.0.1 tests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: python-version: ['pypy-3.10', '3.10', '3.11', '3.12', '3.13'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} allow-prereleases: true - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e .[testing,linkify] - name: Check spec file is up to date run: | python tests/test_cmark_spec/get_cmark_spec.py - name: Run pytest run: | pytest tests/ --cov=markdown_it --cov-report=xml --cov-report=term-missing - name: Upload to Codecov if: matrix.python-version == '3.10' && github.repository == 'executablebooks/markdown-it-py' uses: codecov/codecov-action@v5 with: name: markdown-it-py-pytests flags: pytests files: ./coverage.xml fail_ci_if_error: true token: ${{ secrets.CODECOV_TOKEN }} test-plugins: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.10'] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install markdown-it-py run: | python -m pip install --upgrade pip pip install .[testing] - name: clone and install mdit-py-plugins run: | git clone https://github.com/executablebooks/mdit-py-plugins.git pip install --no-deps -e mdit-py-plugins - name: Run pytest for unit tests of mdit-py-plugins run: cd mdit-py-plugins; pytest benchmark: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.10' - name: Install tox run: | python -m pip install --upgrade pip pip install tox - name: Run benchmark run: tox -e py310-bench-core -- --benchmark-json bench-core.json - name: Upload data uses: actions/upload-artifact@v4 with: name: bench-core path: bench-core.json if-no-files-found: error publish: name: Publish to PyPi needs: [pre-commit, tests] if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') runs-on: ubuntu-latest steps: - name: Checkout source uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.10' - name: install flit run: | pip install flit~=3.4 - name: Build and publish run: | flit publish env: FLIT_USERNAME: __token__ FLIT_PASSWORD: ${{ secrets.PYPI_KEY }} allgood: runs-on: ubuntu-latest needs: - pre-commit - tests steps: - run: echo "Great success!" markdown-it-py-4.2.0/.gitignore 0000664 0000000 0000000 00000003623 15177100052 0016371 0 ustar 00root root 0000000 0000000 # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ pip-wheel-metadata/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # PEP 582; used by e.g. github.com/David-OConnor/pyflow __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ benchmark/extra/ node_modules/ coverage/ demo/ apidoc/ *.log __pycache__/ .ropeproject/ *.egg-info/ .vscode/ .DS_Store docs/api/ uv.lock markdown-it-py-4.2.0/.pre-commit-config.yaml 0000664 0000000 0000000 00000001546 15177100052 0020664 0 ustar 00root root 0000000 0000000 # Install pre-commit hooks via # pre-commit install exclude: > (?x)^( \.vscode/settings\.json| test.*\.md| test.*\.txt| test.*\.html| test.*\.xml| .*commonmark\.json| benchmark/.*\.md| .*/spec\.md )$ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: check-json - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.12 hooks: - id: ruff args: [--fix] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.20.2 hooks: - id: mypy additional_dependencies: [mdurl, typing-extensions] exclude: > (?x)^( benchmarking/.*\.py| docs/.*\.py| scripts/.*\.py| )$ markdown-it-py-4.2.0/.readthedocs.yml 0000664 0000000 0000000 00000000465 15177100052 0017470 0 ustar 00root root 0000000 0000000 version: 2 build: os: ubuntu-22.04 tools: python: "3.10" python: install: - requirements: docs/requirements.txt - method: pip path: . extra_requirements: - linkify - rtd sphinx: configuration: docs/conf.py builder: html fail_on_warning: true markdown-it-py-4.2.0/AGENTS.md 0000664 0000000 0000000 00000034162 15177100052 0015706 0 ustar 00root root 0000000 0000000 # AGENTS.md This file provides guidance for AI coding agents working on the **markdown-it-py** repository. ## Project Overview markdown-it-py is a Python port of [markdown-it](https://github.com/markdown-it/markdown-it), the JavaScript Markdown parser. It provides: - A Markdown parser following the [CommonMark spec](https://commonmark.org/) - Configurable syntax: you can add new rules and even replace existing ones - Pluggable architecture with support for syntax extensions (see [mdit-py-plugins](https://github.com/executablebooks/mdit-py-plugins)) - High performance with efficient parsing algorithms - Safe by default with configurable HTML handling markdown-it-py is designed as a foundation for projects requiring robust Markdown parsing in Python, with the same design principles as the original JavaScript implementation. ## Repository Structure ``` pyproject.toml # Project configuration and dependencies (flit) tox.ini # Tox test environment configuration (use with tox-uv for faster env creation) markdown_it/ # Main source code ├── __init__.py # Package init ├── main.py # MarkdownIt main class ├── token.py # Token dataclass ├── ruler.py # Ruler class for managing rules ├── tree.py # SyntaxTreeNode for AST representation ├── renderer.py # RendererHTML and RendererProtocol ├── parser_core.py # ParserCore - top-level rules executor ├── parser_block.py # ParserBlock - block-level tokenizer ├── parser_inline.py # ParserInline - inline tokenizer ├── utils.py # Utility types (OptionsType, PresetType, etc.) ├── common/ # Common utilities ├── helpers/ # Helper functions ├── presets/ # Configuration presets (commonmark, gfm-like, zero, etc.) ├── rules_core/ # Core parsing rules ├── rules_block/ # Block-level parsing rules ├── rules_inline/ # Inline parsing rules ├── cli/ # Command-line interface └── py.typed # PEP 561 marker tests/ # Test suite ├── test_api/ # API tests ├── test_cmark_spec/ # CommonMark spec compliance tests ├── test_port/ # Port-specific tests ├── test_tree/ # SyntaxTreeNode tests ├── fuzz/ # Fuzzing tests for OSS-Fuzz ├── test_cli.py # CLI tests ├── test_linkify.py # Linkify tests └── test_tree.py # Tree tests docs/ # Documentation source ├── conf.py # Sphinx configuration ├── index.md # Documentation index ├── architecture.md # Design principles ├── using.md # Usage guide ├── plugins.md # Plugin documentation ├── contributing.md # Contributing guide ├── performance.md # Performance benchmarks └── security.md # Security considerations benchmarking/ # Performance benchmarking scripts/ # Utility scripts ``` ## Development Commands All commands should be run via [`tox`](https://tox.wiki) for consistency. The project uses `tox-uv` for faster environment creation. ### Testing ```bash # Run all tests tox # Run tests with specific Python version tox -e py311 # Run tests with plugins tox -e py311-plugins # Run a specific test file tox -- tests/test_api/test_main.py # Run a specific test function tox -- tests/test_api/test_main.py::test_get_rules # Run tests with coverage tox -- --cov=markdown_it --cov-report=html ``` ### Documentation ```bash # Build docs (clean) tox -e docs-clean # Build docs (incremental) tox -e docs-update # Specific builder (e.g., linkcheck) BUILDER=linkcheck tox -e docs-update ``` ### Benchmarking and Profiling ```bash # Run core benchmarks tox -e py311-bench-core # Run package comparison benchmarks tox -e py311-bench-packages # Run profiler tox -e profile ``` ### Fuzzing ```bash # Run fuzzer on testcase file tox -e fuzz path/to/testcase ``` ### Code Quality ```bash # Run pre-commit hooks on all files pre-commit run --all-files # Type checking (via pre-commit) pre-commit run mypy --all-files # Linting and formatting (via pre-commit) pre-commit run ruff --all-files pre-commit run ruff-format --all-files ``` ## Code Style Guidelines - **Formatter/Linter**: Ruff (configured in `pyproject.toml`) - **Type Checking**: Mypy with strict settings (configured in `pyproject.toml`) - **Pre-commit**: Use pre-commit hooks for consistent code style (`.pre-commit-config.yaml`) ### Best Practices - **Type annotations**: Use complete type annotations for all function signatures. The codebase uses strict mypy settings. - **Docstrings**: Use Google-style or Sphinx-style docstrings. Types are not required in docstrings as they should be in type hints. - **Pure functions**: Where possible, write pure functions without side effects. - **Immutability**: Prefer immutable data structures. The `Token` class uses dataclass with appropriate mutability. - **Testing**: Write tests for all new functionality. Use `pytest-regressions` for output comparison tests. ### Type Annotation Example ```python from __future__ import annotations from typing import Sequence def parse_blocks( state: StateBlock, start_line: int, end_line: int, silent: bool = False ) -> bool: """Parse block-level content. :param state: The parser state object :param start_line: Starting line number :param end_line: Ending line number :param silent: If True, only validate without generating tokens :return: True if parsing succeeded """ ... ``` ## Architecture Overview ### Parsing Pipeline markdown-it-py follows a multi-stage parsing pipeline: ``` Markdown → Tokens → HTML ``` The parsing happens through three nested chains: 1. **Core Chain** (`parser_core.py`): Top-level rules that orchestrate the parsing 2. **Block Chain** (`parser_block.py`): Parse block-level content (headings, lists, code blocks, etc.) 3. **Inline Chain** (`parser_inline.py`): Parse inline content (emphasis, links, code spans, etc.) ### Token Stream Instead of a traditional AST, markdown-it-py uses a **token stream** representation: - Tokens are a simple sequence (list) - Opening and closing tags are separate tokens - Inline containers have nested tokens in their `.children` property - This design follows the KISS principle and allows easy manipulation ### Key Components #### MarkdownIt Class (`main.py`) The main entry point for parsing: - `parse()`: Parse markdown and return token stream - `render()`: Parse and render to HTML - `use()`: Add plugins - `enable()` / `disable()`: Control rules - `set()`: Set options #### Ruler Class (`ruler.py`) Manages parsing rules: - Rules can be enabled/disabled by name - Rules can be inserted at specific positions - Each parser (core/block/inline) has its own Ruler instance #### Token Class (`token.py`) Represents a single token in the stream: - `type`: Token type (e.g., "paragraph_open", "text", "heading_close") - `tag`: HTML tag to use for rendering - `attrs`: Attributes for the HTML tag - `content`: Raw content - `children`: Nested tokens for inline containers - `level`: Nesting level #### Renderer (`renderer.py`) Converts token stream to HTML: - `render()`: Convert full token stream to HTML - `renderToken()`: Render a single token - Custom render rules can be added via `add_render_rule()` ### Data Flow ``` Input Markdown ↓ Core Rules (normalize, etc.) ↓ Block Parser → Block Tokens ↓ Core Rules (intermediate) ↓ Inline Parser → Inline Tokens (for each block token with "inline" type) ↓ Core Rules (final: abbreviations, footnotes, linkify, etc.) ↓ Token Stream ↓ Renderer ↓ HTML Output ``` ## Testing Guidelines ### Test Structure - Tests use `pytest` with fixtures from `conftest.py` files - CommonMark spec tests are in `tests/test_cmark_spec/` - Port-specific tests verify JavaScript markdown-it parity - Regression testing uses `pytest-regressions` for output comparison - Fuzzing tests are in `tests/fuzz/` for integration with OSS-Fuzz ### Writing Tests 1. For API tests, add to appropriate file in `tests/test_api/` 2. For new syntax/rules, add test cases to `tests/test_port/` 3. For CommonMark compliance, run the spec test updater 4. Use `file_regression` fixture for comparing output against stored fixtures 5. Use parameterization for multiple test scenarios ### Test Best Practices - **Test coverage**: Write tests for all new functionality and bug fixes - **Isolation**: Each test should be independent - **Descriptive names**: Test function names should describe what is being tested - **Regression testing**: Use `file_regression.check()` for complex output comparisons - **Parametrization**: Use `@pytest.mark.parametrize` for multiple test scenarios ### Example Test Pattern ```python import pytest from markdown_it import MarkdownIt def test_basic_parsing(): md = MarkdownIt() result = md.render("# Heading\n\nParagraph") assert "
Paragraph
" in result @pytest.mark.parametrize( "input_text,expected", [ ("**bold**", "bold"), ("*italic*", "italic"), ] ) def test_emphasis(input_text, expected): md = MarkdownIt() result = md.render(input_text) assert expected in result ``` ## Commit Message Format Use this format: ```
Batch: $ markdown-it README.md README.footer.md > index.html ``` ## References / Thanks Big thanks to the authors of [markdown-it]: - Alex Kocharin [github/rlidwka](https://github.com/rlidwka) - Vitaly Puzrin [github/puzrin](https://github.com/puzrin) Also [John MacFarlane](https://github.com/jgm) for his work on the CommonMark spec and reference implementations. [github-ci]: https://github.com/executablebooks/markdown-it-py/actions/workflows/tests.yml/badge.svg?branch=master [github-link]: https://github.com/executablebooks/markdown-it-py [pypi-badge]: https://img.shields.io/pypi/v/markdown-it-py.svg [pypi-link]: https://pypi.org/project/markdown-it-py [conda-badge]: https://anaconda.org/conda-forge/markdown-it-py/badges/version.svg [conda-link]: https://anaconda.org/conda-forge/markdown-it-py [codecov-badge]: https://codecov.io/gh/executablebooks/markdown-it-py/branch/master/graph/badge.svg [codecov-link]: https://codecov.io/gh/executablebooks/markdown-it-py [install-badge]: https://img.shields.io/pypi/dw/markdown-it-py?label=pypi%20installs [install-link]: https://pypistats.org/packages/markdown-it-py [CommonMark spec]: http://spec.commonmark.org/ [markdown-it]: https://github.com/markdown-it/markdown-it [markdown-it-readme]: https://github.com/markdown-it/markdown-it/blob/master/README.md [md-security]: https://markdown-it-py.readthedocs.io/en/latest/security.html [md-performance]: https://markdown-it-py.readthedocs.io/en/latest/performance.html [md-plugins]: https://markdown-it-py.readthedocs.io/en/latest/plugins.html markdown-it-py-4.2.0/SECURITY.md 0000664 0000000 0000000 00000001106 15177100052 0016164 0 ustar 00root root 0000000 0000000 # Security Policy ## Supported Versions We generally only support the latest major release, although critical bug fixes can be released for older versions. ## Reporting a Vulnerability To report a security issue, please emailmarkdown input
| **test** |
| test |
` tags? Can a list be partially "loose" and partially "tight"? What should we do with a list like this? ``` markdown 1. one 2. two 3. three ``` Or this? ``` markdown 1. one - a - b 2. two ``` (There are some relevant comments by John Gruber [here](http://article.gmane.org/gmane.text.markdown.general/2554).) 5. Can list markers be indented? Can ordered list markers be right-aligned? ``` markdown 8. item 1 9. item 2 10. item 2a ``` 6. Is this one list with a thematic break in its second item, or two lists separated by a thematic break? ``` markdown * a * * * * * * b ``` 7. When list markers change from numbers to bullets, do we have two lists or one? (The Markdown syntax description suggests two, but the perl scripts and many other implementations produce one.) ``` markdown 1. fee 2. fie - foe - fum ``` 8. What are the precedence rules for the markers of inline structure? For example, is the following a valid link, or does the code span take precedence ? ``` markdown [a backtick (`)](/url) and [another backtick (`)](/url). ``` 9. What are the precedence rules for markers of emphasis and strong emphasis? For example, how should the following be parsed? ``` markdown *foo *bar* baz* ``` 10. What are the precedence rules between block-level and inline-level structure? For example, how should the following be parsed? ``` markdown - `a long code span can contain a hyphen like this - and it can screw things up` ``` 11. Can list items include section headings? (`Markdown.pl` does not allow this, but does allow blockquotes to include headings.) ``` markdown - # Heading ``` 12. Can list items be empty? ``` markdown * a * * b ``` 13. Can link references be defined inside block quotes or list items? ``` markdown > Blockquote [foo]. > > [foo]: /url ``` 14. If there are multiple definitions for the same reference, which takes precedence? ``` markdown [foo]: /url1 [foo]: /url2 [foo][] ``` In the absence of a spec, early implementers consulted `Markdown.pl` to resolve these ambiguities. But `Markdown.pl` was quite buggy, and gave manifestly bad results in many cases, so it was not a satisfactory replacement for a spec. Because there is no unambiguous spec, implementations have diverged considerably. As a result, users are often surprised to find that a document that renders one way on one system (say, a GitHub wiki) renders differently on another (say, converting to docbook using pandoc). To make matters worse, because nothing in Markdown counts as a "syntax error," the divergence often isn't discovered right away. ## About this document This document attempts to specify Markdown syntax unambiguously. It contains many examples with side-by-side Markdown and HTML. These are intended to double as conformance tests. An accompanying script `spec_tests.py` can be used to run the tests against any Markdown program: python test/spec_tests.py --spec spec.txt --program PROGRAM Since this document describes how Markdown is to be parsed into an abstract syntax tree, it would have made sense to use an abstract representation of the syntax tree instead of HTML. But HTML is capable of representing the structural distinctions we need to make, and the choice of HTML for the tests makes it possible to run the tests against an implementation without writing an abstract syntax tree renderer. This document is generated from a text file, `spec.txt`, written in Markdown with a small extension for the side-by-side tests. The script `tools/makespec.py` can be used to convert `spec.txt` into HTML or CommonMark (which can then be converted into other formats). In the examples, the `→` character is used to represent tabs. # Preliminaries ## Characters and lines Any sequence of [characters] is a valid CommonMark document. A [character](@) is a Unicode code point. Although some code points (for example, combining accents) do not correspond to characters in an intuitive sense, all code points count as characters for purposes of this spec. This spec does not specify an encoding; it thinks of lines as composed of [characters] rather than bytes. A conforming parser may be limited to a certain encoding. A [line](@) is a sequence of zero or more [characters] other than newline (`U+000A`) or carriage return (`U+000D`), followed by a [line ending] or by the end of file. A [line ending](@) is a newline (`U+000A`), a carriage return (`U+000D`) not followed by a newline, or a carriage return and a following newline. A line containing no characters, or a line containing only spaces (`U+0020`) or tabs (`U+0009`), is called a [blank line](@). The following definitions of character classes will be used in this spec: A [whitespace character](@) is a space (`U+0020`), tab (`U+0009`), newline (`U+000A`), line tabulation (`U+000B`), form feed (`U+000C`), or carriage return (`U+000D`). [Whitespace](@) is a sequence of one or more [whitespace characters]. A [Unicode whitespace character](@) is any code point in the Unicode `Zs` general category, or a tab (`U+0009`), carriage return (`U+000D`), newline (`U+000A`), or form feed (`U+000C`). [Unicode whitespace](@) is a sequence of one or more [Unicode whitespace characters]. A [space](@) is `U+0020`. A [non-whitespace character](@) is any character that is not a [whitespace character]. An [ASCII punctuation character](@) is `!`, `"`, `#`, `$`, `%`, `&`, `'`, `(`, `)`, `*`, `+`, `,`, `-`, `.`, `/` (U+0021–2F), `:`, `;`, `<`, `=`, `>`, `?`, `@` (U+003A–0040), `[`, `\`, `]`, `^`, `_`, `` ` `` (U+005B–0060), `{`, `|`, `}`, or `~` (U+007B–007E). A [punctuation character](@) is an [ASCII punctuation character] or anything in the general Unicode categories `Pc`, `Pd`, `Pe`, `Pf`, `Pi`, `Po`, or `Ps`. ## Tabs Tabs in lines are not expanded to [spaces]. However, in contexts where whitespace helps to define block structure, tabs behave as if they were replaced by spaces with a tab stop of 4 characters. Thus, for example, a tab can be used instead of four spaces in an indented code block. (Note, however, that internal tabs are passed through as literal tabs, not expanded to spaces.) ```````````````````````````````` example →foo→baz→→bim .
foo→baz→→bim
````````````````````````````````
```````````````````````````````` example
→foo→baz→→bim
.
foo→baz→→bim
````````````````````````````````
```````````````````````````````` example
a→a
ὐ→a
.
a→a
ὐ→a
````````````````````````````````
In the following example, a continuation paragraph of a list
item is indented with a tab; this has exactly the same effect
as indentation with four spaces would:
```````````````````````````````` example
- foo
→bar
.
foo
bar
foo
bar
```````````````````````````````` ```````````````````````````````` example -→→foo .foo
foo
foo
bar
````````````````````````````````
```````````````````````````````` example
- foo
- bar
→ - baz
.
+++
```````````````````````````````` ```````````````````````````````` example === .===
```````````````````````````````` Not enough characters: ```````````````````````````````` example -- ** __ .-- ** __
```````````````````````````````` One to three spaces indent are allowed: ```````````````````````````````` example *** *** *** .***
````````````````````````````````
```````````````````````````````` example
Foo
***
.
Foo ***
```````````````````````````````` More than three characters may be used: ```````````````````````````````` example _____________________________________ ._ _ _ _ a
a------
---a---
```````````````````````````````` It is required that all of the [non-whitespace characters] be the same. So, this is not a thematic break: ```````````````````````````````` example *-* .-
```````````````````````````````` Thematic breaks do not need blank lines before or after: ```````````````````````````````` example - foo *** - bar .Foo
bar
```````````````````````````````` If a line of dashes that meets the above conditions for being a thematic break could also be interpreted as the underline of a [setext heading], the interpretation as a [setext heading] takes precedence. Thus, for example, this is a setext heading, not a paragraph followed by a thematic break: ```````````````````````````````` example Foo --- bar .bar
```````````````````````````````` When both a thematic break and a list item are possible interpretations of a line, the thematic break takes precedence: ```````````````````````````````` example * Foo * * * * Bar .####### foo
```````````````````````````````` At least one space is required between the `#` characters and the heading's contents, unless the heading is empty. Note that many implementations currently do not require the space. However, the space was required by the [original ATX implementation](http://www.aaronsw.com/2002/atx/atx.py), and it helps prevent things like the following from being parsed as headings: ```````````````````````````````` example #5 bolt #hashtag .#5 bolt
#hashtag
```````````````````````````````` This is not a heading, because the first `#` is escaped: ```````````````````````````````` example \## foo .## foo
```````````````````````````````` Contents are parsed as inlines: ```````````````````````````````` example # foo *bar* \*baz\* .# foo
````````````````````````````````
```````````````````````````````` example
foo
# bar
.
foo # bar
```````````````````````````````` A closing sequence of `#` characters is optional: ```````````````````````````````` example ## foo ## ### bar ### .Foo bar
Bar foo
```````````````````````````````` ATX headings can be empty: ```````````````````````````````` example ## # ### ### . ```````````````````````````````` ## Setext headings A [setext heading](@) consists of one or more lines of text, each containing at least one [non-whitespace character], with no more than 3 spaces indentation, followed by a [setext heading underline]. The lines of text must be such that, were they not followed by the setext heading underline, they would be interpreted as a paragraph: they cannot be interpretable as a [code fence], [ATX heading][ATX headings], [block quote][block quotes], [thematic break][thematic breaks], [list item][list items], or [HTML block][HTML blocks]. A [setext heading underline](@) is a sequence of `=` characters or a sequence of `-` characters, with no more than 3 spaces indentation and any number of trailing spaces. If a line containing a single `-` can be interpreted as an empty [list items], it should be interpreted this way and not as a [setext heading underline]. The heading is a level 1 heading if `=` characters are used in the [setext heading underline], and a level 2 heading if `-` characters are used. The contents of the heading are the result of parsing the preceding lines of text as CommonMark inline content. In general, a setext heading need not be preceded or followed by a blank line. However, it cannot interrupt a paragraph, so when a setext heading comes after a paragraph, a blank line is needed between them. Simple examples: ```````````````````````````````` example Foo *bar* ========= Foo *bar* --------- .Foo
---
Foo
Foo ---
```````````````````````````````` The setext heading underline cannot contain internal spaces: ```````````````````````````````` example Foo = = Foo --- - .Foo = =
Foo
`
of dashes"/>
```````````````````````````````` The setext heading underline cannot be a [lazy continuation line] in a list item or block quote: ```````````````````````````````` example > Foo --- .Foo
```````````````````````````````` ```````````````````````````````` example - Foo --- .foo bar ===
Baz
```````````````````````````````` Setext headings cannot be empty: ```````````````````````````````` example ==== .====
```````````````````````````````` Setext heading text lines must not be interpretable as block constructs other than paragraphs. So, the line of dashes in these examples gets interpreted as a thematic break: ```````````````````````````````` example --- --- .foo
foo
Foo
baz
```````````````````````````````` Authors who want interpretation 2 can put blank lines around the thematic break, ```````````````````````````````` example Foo bar --- baz .Foo bar
baz
```````````````````````````````` or use a thematic break that cannot count as a [setext heading underline], such as ```````````````````````````````` example Foo bar * * * baz .Foo bar
baz
```````````````````````````````` Authors who want interpretation 3 can use backslash escapes: ```````````````````````````````` example Foo bar \--- baz .Foo bar --- baz
```````````````````````````````` ## Indented code blocks An [indented code block](@) is composed of one or more [indented chunks] separated by blank lines. An [indented chunk](@) is a sequence of non-blank lines, each indented four or more spaces. The contents of the code block are the literal contents of the lines, including trailing [line endings], minus four spaces of indentation. An indented code block has no [info string]. An indented code block cannot interrupt a paragraph, so there must be a blank line between a paragraph and a following indented code block. (A blank line is not needed, however, between a code block and a following paragraph.) ```````````````````````````````` example a simple indented code block .a simple
indented code block
````````````````````````````````
If there is any ambiguity between an interpretation of indentation
as a code block and as indicating that material belongs to a [list
item][list items], the list item interpretation takes precedence:
```````````````````````````````` example
- foo
bar
.
foo
bar
foo
<a/>
*hi*
- one
````````````````````````````````
Here we have three chunks separated by blank lines:
```````````````````````````````` example
chunk1
chunk2
chunk3
.
chunk1
chunk2
chunk3
````````````````````````````````
Any initial spaces beyond four will be included in the content, even
in interior blank lines:
```````````````````````````````` example
chunk1
chunk2
.
chunk1
chunk2
````````````````````````````````
An indented code block cannot interrupt a paragraph. (This
allows hanging indents and the like.)
```````````````````````````````` example
Foo
bar
.
Foo bar
```````````````````````````````` However, any non-blank line with fewer than four leading spaces ends the code block immediately. So a paragraph may occur immediately after indented code: ```````````````````````````````` example foo bar .foo
bar
```````````````````````````````` And indented code can occur immediately before and after other kinds of blocks: ```````````````````````````````` example # Heading foo Heading ------ foo ---- .foo
foo
foo
bar
````````````````````````````````
Blank lines preceding or following an indented code block
are not included in it:
```````````````````````````````` example
foo
.
foo
````````````````````````````````
Trailing spaces are included in the code block's content:
```````````````````````````````` example
foo
.
foo
````````````````````````````````
## Fenced code blocks
A [code fence](@) is a sequence
of at least three consecutive backtick characters (`` ` ``) or
tildes (`~`). (Tildes and backticks cannot be mixed.)
A [fenced code block](@)
begins with a code fence, indented no more than three spaces.
The line with the opening code fence may optionally contain some text
following the code fence; this is trimmed of leading and trailing
whitespace and called the [info string](@). If the [info string] comes
after a backtick fence, it may not contain any backtick
characters. (The reason for this restriction is that otherwise
some inline code would be incorrectly interpreted as the
beginning of a fenced code block.)
The content of the code block consists of all subsequent lines, until
a closing [code fence] of the same type as the code block
began with (backticks or tildes), and with at least as many backticks
or tildes as the opening code fence. If the leading code fence is
indented N spaces, then up to N spaces of indentation are removed from
each line of the content (if present). (If a content line is not
indented, it is preserved unchanged. If it is indented less than N
spaces, all of the indentation is removed.)
The closing code fence may be indented up to three spaces, and may be
followed only by spaces, which are ignored. If the end of the
containing block (or document) is reached and no closing code fence
has been found, the code block contains all of the lines after the
opening code fence until the end of the containing block (or
document). (An alternative spec would require backtracking in the
event that a closing code fence is not found. But this makes parsing
much less efficient, and there seems to be no real down side to the
behavior described here.)
A fenced code block may interrupt a paragraph, and does not require
a blank line either before or after.
The content of a code fence is treated as literal text, not parsed
as inlines. The first word of the [info string] is typically used to
specify the language of the code sample, and rendered in the `class`
attribute of the `code` tag. However, this spec does not mandate any
particular treatment of the [info string].
Here is a simple example with backticks:
```````````````````````````````` example
```
<
>
```
.
<
>
````````````````````````````````
With tildes:
```````````````````````````````` example
~~~
<
>
~~~
.
<
>
````````````````````````````````
Fewer than three backticks is not enough:
```````````````````````````````` example
``
foo
``
.
foo
aaa
~~~
````````````````````````````````
```````````````````````````````` example
~~~
aaa
```
~~~
.
aaa
```
````````````````````````````````
The closing code fence must be at least as long as the opening fence:
```````````````````````````````` example
````
aaa
```
``````
.
aaa
```
````````````````````````````````
```````````````````````````````` example
~~~~
aaa
~~~
~~~~
.
aaa
~~~
````````````````````````````````
Unclosed code blocks are closed by the end of the document
(or the enclosing [block quote][block quotes] or [list item][list items]):
```````````````````````````````` example
```
.
````````````````````````````````
```````````````````````````````` example
`````
```
aaa
.
```
aaa
````````````````````````````````
```````````````````````````````` example
> ```
> aaa
bbb
.
aaa
bbb
```````````````````````````````` A code block can have all empty lines as its content: ```````````````````````````````` example ``` ``` .
````````````````````````````````
A code block can be empty:
```````````````````````````````` example
```
```
.
````````````````````````````````
Fences can be indented. If the opening fence is indented,
content lines will have equivalent opening indentation removed,
if present:
```````````````````````````````` example
```
aaa
aaa
```
.
aaa
aaa
````````````````````````````````
```````````````````````````````` example
```
aaa
aaa
aaa
```
.
aaa
aaa
aaa
````````````````````````````````
```````````````````````````````` example
```
aaa
aaa
aaa
```
.
aaa
aaa
aaa
````````````````````````````````
Four spaces indentation produces an indented code block:
```````````````````````````````` example
```
aaa
```
.
```
aaa
```
````````````````````````````````
Closing fences may be indented by 0-3 spaces, and their indentation
need not match that of the opening fence:
```````````````````````````````` example
```
aaa
```
.
aaa
````````````````````````````````
```````````````````````````````` example
```
aaa
```
.
aaa
````````````````````````````````
This is not a closing fence, because it is indented 4 spaces:
```````````````````````````````` example
```
aaa
```
.
aaa
```
````````````````````````````````
Code fences (opening and closing) cannot contain internal spaces:
```````````````````````````````` example
``` ```
aaa
.
aaa
aaa
~~~ ~~
````````````````````````````````
Fenced code blocks can interrupt paragraphs, and can be followed
directly by paragraphs, without a blank line between:
```````````````````````````````` example
foo
```
bar
```
baz
.
foo
bar
baz
```````````````````````````````` Other blocks can also occur before and after fenced code blocks without an intervening blank line: ```````````````````````````````` example foo --- ~~~ bar ~~~ # baz .bar
def foo(x)
return 3
end
````````````````````````````````
```````````````````````````````` example
~~~~ ruby startline=3 $%@#$
def foo(x)
return 3
end
~~~~~~~
.
def foo(x)
return 3
end
````````````````````````````````
```````````````````````````````` example
````;
````
.
````````````````````````````````
[Info strings] for backtick code blocks cannot contain backticks:
```````````````````````````````` example
``` aa ```
foo
.
aa
foo
foo
````````````````````````````````
Closing code fences cannot have [info strings]:
```````````````````````````````` example
```
``` aaa
```
.
``` aaa
````````````````````````````````
## HTML blocks
An [HTML block](@) is a group of lines that is treated
as raw HTML (and will not be escaped in HTML output).
There are seven kinds of [HTML block], which can be defined by their
start and end conditions. The block begins with a line that meets a
[start condition](@) (after up to three spaces optional indentation).
It ends with the first subsequent line that meets a matching [end
condition](@), or the last line of the document, or the last line of
the [container block](#container-blocks) containing the current HTML
block, if no line is encountered that meets the [end condition]. If
the first line meets both the [start condition] and the [end
condition], the block will contain just that line.
1. **Start condition:** line begins with the string ``, ``, or `` (case-insensitive; it
need not match the start tag).
2. **Start condition:** line begins with the string ``.
3. **Start condition:** line begins with the string ``.\
**End condition:** line contains the string `?>`.
4. **Start condition:** line begins with the string ``.
5. **Start condition:** line begins with the string
``.
6. **Start condition:** line begins the string `<` or ``
followed by one of the strings (case-insensitive) `address`,
`article`, `aside`, `base`, `basefont`, `blockquote`, `body`,
`caption`, `center`, `col`, `colgroup`, `dd`, `details`, `dialog`,
`dir`, `div`, `dl`, `dt`, `fieldset`, `figcaption`, `figure`,
`footer`, `form`, `frame`, `frameset`,
`h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `head`, `header`, `hr`,
`html`, `iframe`, `legend`, `li`, `link`, `main`, `menu`, `menuitem`,
`nav`, `noframes`, `ol`, `optgroup`, `option`, `p`, `param`,
`section`, `source`, `summary`, `table`, `tbody`, `td`,
`tfoot`, `th`, `thead`, `title`, `tr`, `track`, `ul`, followed
by [whitespace], the end of the line, the string `>`, or
the string `/>`.\
**End condition:** line is followed by a [blank line].
7. **Start condition:** line begins with a complete [open tag]
(with any [tag name] other than `script`,
`style`, or `pre`) or a complete [closing tag],
followed only by [whitespace] or the end of the line.\
**End condition:** line is followed by a [blank line].
HTML blocks continue until they are closed by their appropriate
[end condition], or the last line of the document or other [container
block](#container-blocks). This means any HTML **within an HTML
block** that might otherwise be recognised as a start condition will
be ignored by the parser and passed through as-is, without changing
the parser's state.
For instance, `` within a HTML block started by `