--- /dev/null
+comment: off
--- /dev/null
+[run]
+omit=
+ *site-packages*
+ tests/*
+ markdown/test_tools.py
+ markdown/pep562.py
--- /dev/null
+tests/basic/** linguist-vendored
+tests/extensions/** linguist-vendored
+tests/misc/** linguist-vendored
+tests/options/** linguist-vendored
+tests/php/** linguist-vendored
+tests/pl/** linguist-vendored
+tests/safe_mode/** linguist-vendored
--- /dev/null
+name: deploy
+
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+
+ pypi:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.7
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip setuptools wheel
+ - name: Build
+ run: |
+ python setup.py bdist_wheel sdist --formats gztar
+ - name: Publish
+ if: success()
+ uses: pypa/gh-action-pypi-publish@v1.1.0
+ with:
+ user: __token__
+ password: ${{ secrets.PYPI_PASSWORD }}
+
+ ghpages:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Python
+ uses: actions/setup-python@v1
+ with:
+ python-version: 3.7
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip setuptools
+ python -m pip install -r doc-requirements.txt
+ - name: Build
+ run: |
+ python -m mkdocs build --clean --verbose
+ - name: Publish
+ if: success()
+ uses: peaceiris/actions-gh-pages@v3
+ with:
+ deploy_key: ${{ secrets.PAGES_DEPLOY_KEY }}
+ external_repository: Python-Markdown/Python-Markdown.github.io
+ publish_branch: master
+ publish_dir: ./site
--- /dev/null
+# This workflow will install dependencies and run tests/linters with a matrix of tox environments.
+
+name: CI
+
+on:
+ push:
+ branches:
+ - master
+ tags:
+ - '**'
+ pull_request:
+ branches:
+ - '**'
+
+jobs:
+ test:
+
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ max-parallel: 4
+ matrix:
+ tox-env: [py36, py37, py38, py39, pypy3, pygments]
+ include:
+ - tox-env: py36
+ python-version: 3.6
+ - tox-env: py37
+ python-version: 3.7
+ - tox-env: py38
+ python-version: 3.8
+ - tox-env: py39
+ python-version: 3.9
+ - tox-env: pypy3
+ python-version: pypy3
+ - tox-env: pygments
+ python-version: 3.7
+
+ env:
+ TOXENV: ${{ matrix.tox-env }}
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install dependencies
+ run: |
+ sudo apt-get install libtidy-dev
+ python -m pip install --upgrade pip tox coverage codecov
+ - name: Run tox
+ run: python -m tox
+ - name: Upload Results
+ if: success()
+ uses: codecov/codecov-action@v1
+ with:
+ file: ./coverage.xml
+ flags: unittests
+ name: ${{ matrix.tox-env }}
+ fail_ci_if_error: false
+
+ lint:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ max-parallel: 4
+ matrix:
+ tox-env: [flake8, pep517check, checkspelling, checklinks]
+
+ env:
+ TOXENV: ${{ matrix.tox-env }}
+
+ # Allow checklinks to fail
+ continue-on-error: ${{ matrix.tox-env == 'checklinks' }}
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Setup Python
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.7
+ - name: Setup Node
+ if: ${{ matrix.tox-env == 'checklinks' }}
+ uses: actions/setup-node@v1
+ with:
+ node-version: '10'
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip tox
+ if [[ "$TOXENV" == 'checklinks' ]]; then npm install -g markdown-link-check; fi
+ if [[ "$TOXENV" == 'checkspelling' ]]; then sudo apt-get install aspell aspell-en; fi
+ - name: Run tox
+ run: python -m tox
--- /dev/null
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.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/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+test-output.html
+
+# Translations
+*.mo
+*.pot
+
+# Scrapy stuff:
+.scrapy
+
+# PyBuilder
+target/
+
+# IPython Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# virtualenv
+venv/
+ENV/
+
+# MkDocs documentation
+site/
--- /dev/null
+facelessuser
+mitya
+waylan
+Abrahamsen
+Altmayer
+API
+Artem
+aspell
+Babelmark
+backtick
+backticks
+Balogh
+BlockParser
+Blockprocessor
+Blockprocessors
+blockquote
+blockquotes
+boolean
+CamelCase
+Chodarev
+CLI
+CodeHilite
+codehilite
+Cogumbreiro
+convertFile
+CSS
+dedent
+deliminators
+deregister
+Dmitry
+docdata
+ElementTree
+encodings
+extendMarkdown
+Fauske
+formatter
+Fortin
+GitHub
+globals
+Gruber
+GSoC
+hacky
+HeaderId
+HTTPS
+html
+implementers
+InlineProcessor
+Jiryu
+JSON
+Kjell
+Krech
+kwargs
+Limberg
+MacOS
+Magne
+MAILTO
+makeExtension
+Manfed
+markdownFromFile
+Maruku
+md
+metadata
+MkDocs
+multi
+MultiMarkdown
+munge
+namespace
+NanoDOM
+Neale
+nosetests
+OrderedDict
+OrderedDicts
+OSX
+ol
+Ph
+PHP
+Posix
+postprocessor
+postprocessors
+prepend
+prepended
+preprocessor
+preprocessors
+Pygments
+PyPI
+PyPy
+PYTHONPATH
+PyTidyLib
+PyYAML
+rc
+refactor
+refactored
+refactors
+registerExtension
+richeland
+RSS
+rST
+ryneeverett
+sanitizer
+sanitizers
+Sauder
+schemeless
+setuptools
+Sergej
+serializer
+serializers
+Shachnev
+sitemap
+slugify
+SmartyPants
+Sourceforge
+StackOverflow
+Stansifer
+stdout
+Stelios
+Stienstra
+subclasses
+SuperFences
+svn
+Swartz
+Szakmeister
+Takteyev
+Tiago
+toc
+tokenized
+tox
+Trac
+traceback
+Tredinnick
+Treeprocessor
+Treeprocessors
+tuple
+tuples
+unclosed
+unescape
+unescaping
+unittest
+unordered
+untrusted
+UTF
+uTidylib
+versa
+versioning
+Waylan
+whitelist
+whitespace
+WikiLink
+WikiLinks
+Wolever
+workflow
+Xanthakis
+XHTML
+xhtml
+YAML
+Yunusov
+inline
+wiki
+JavaScript
+plugin
+plugins
+configs
+pre
--- /dev/null
+# Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of
+fostering an open and welcoming community, we pledge to respect all people who
+contribute through reporting issues, posting feature requests, updating
+documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free
+experience for everyone, regardless of level of experience, gender, gender
+identity and expression, sexual orientation, disability, personal appearance,
+body size, race, ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery
+* Personal attacks
+* Trolling or insulting/derogatory comments
+* Public or private harassment
+* Publishing other's private information, such as physical or electronic
+ addresses, without explicit permission
+* Other unethical or unprofessional conduct
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+By adopting this Code of Conduct, project maintainers commit themselves to
+fairly and consistently applying these principles to every aspect of managing
+this project. Project maintainers who do not follow or enforce the Code of
+Conduct may be permanently removed from the project team.
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting a project maintainer at markdown@freewisdom.org. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. Maintainers are
+obligated to maintain confidentiality with regard to the reporter of an
+incident.
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 1.3.0, available at
+[https://www.contributor-covenant.org/version/1/3/0/][version]
+
+[homepage]: https://www.contributor-covenant.org/
+[version]: https://www.contributor-covenant.org/version/1/3/0/
+++ /dev/null
-Metadata-Version: 2.1
-Name: Markdown
-Version: 3.2.2
-Summary: Python implementation of Markdown.
-Home-page: https://Python-Markdown.github.io/
-Author: Manfred Stienstra, Yuri takhteyev and Waylan limberg
-Author-email: waylan.limberg@icloud.com
-Maintainer: Waylan Limberg
-Maintainer-email: waylan.limberg@icloud.com
-License: BSD License
-Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-3.2.2-py2.py3-none-any.whl
-Description:
- This is a Python implementation of John Gruber's Markdown_.
- It is almost completely compliant with the reference implementation,
- though there are a few known issues. See Features_ for information
- on what exactly is supported and what is not. Additional features are
- supported by the `Available Extensions`_.
-
- .. _Markdown: https://daringfireball.net/projects/markdown/
- .. _Features: https://Python-Markdown.github.io#features
- .. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
-
- Support
- =======
-
- You may report bugs, ask for help, and discuss various other issues on
- the `bug tracker`_.
-
- .. _`bug tracker`: https://github.com/Python-Markdown/markdown/issues
-
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Topic :: Communications :: Email :: Filters
-Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries
-Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
-Classifier: Topic :: Software Development :: Documentation
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Topic :: Text Processing :: Filters
-Classifier: Topic :: Text Processing :: Markup :: HTML
-Requires-Python: >=3.5
-Provides-Extra: testing
+++ /dev/null
-INSTALL.md
-LICENSE.md
-MANIFEST.in
-README.md
-doc-requirements.txt
-makefile
-mkdocs.yml
-pyproject.toml
-setup.cfg
-setup.py
-tox.ini
-Markdown.egg-info/PKG-INFO
-Markdown.egg-info/SOURCES.txt
-Markdown.egg-info/dependency_links.txt
-Markdown.egg-info/entry_points.txt
-Markdown.egg-info/requires.txt
-Markdown.egg-info/top_level.txt
-docs/authors.md
-docs/cli.md
-docs/contributing.md
-docs/favicon.ico
-docs/index.md
-docs/install.md
-docs/py.png
-docs/reference.md
-docs/test_tools.md
-docs/change_log/index.md
-docs/change_log/release-2.0.md
-docs/change_log/release-2.1.md
-docs/change_log/release-2.2.md
-docs/change_log/release-2.3.md
-docs/change_log/release-2.4.md
-docs/change_log/release-2.5.md
-docs/change_log/release-2.6.md
-docs/change_log/release-3.0.md
-docs/change_log/release-3.1.md
-docs/change_log/release-3.2.md
-docs/extensions/abbreviations.md
-docs/extensions/admonition.md
-docs/extensions/api.md
-docs/extensions/attr_list.md
-docs/extensions/code_hilite.md
-docs/extensions/definition_lists.md
-docs/extensions/extra.md
-docs/extensions/fenced_code_blocks.md
-docs/extensions/footnotes.md
-docs/extensions/index.md
-docs/extensions/legacy_attrs.md
-docs/extensions/legacy_em.md
-docs/extensions/md_in_html.md
-docs/extensions/meta_data.md
-docs/extensions/nl2br.md
-docs/extensions/sane_lists.md
-docs/extensions/smarty.md
-docs/extensions/tables.md
-docs/extensions/toc.md
-docs/extensions/wikilinks.md
-markdown/__init__.py
-markdown/__main__.py
-markdown/__meta__.py
-markdown/blockparser.py
-markdown/blockprocessors.py
-markdown/core.py
-markdown/inlinepatterns.py
-markdown/pep562.py
-markdown/postprocessors.py
-markdown/preprocessors.py
-markdown/serializers.py
-markdown/test_tools.py
-markdown/treeprocessors.py
-markdown/util.py
-markdown/extensions/__init__.py
-markdown/extensions/abbr.py
-markdown/extensions/admonition.py
-markdown/extensions/attr_list.py
-markdown/extensions/codehilite.py
-markdown/extensions/def_list.py
-markdown/extensions/extra.py
-markdown/extensions/fenced_code.py
-markdown/extensions/footnotes.py
-markdown/extensions/legacy_attrs.py
-markdown/extensions/legacy_em.py
-markdown/extensions/md_in_html.py
-markdown/extensions/meta.py
-markdown/extensions/nl2br.py
-markdown/extensions/sane_lists.py
-markdown/extensions/smarty.py
-markdown/extensions/tables.py
-markdown/extensions/toc.py
-markdown/extensions/wikilinks.py
-tests/__init__.py
-tests/test_apis.py
-tests/test_extensions.py
-tests/test_legacy.py
-tests/test_meta.py
-tests/basic/amps-and-angle-encoding.html
-tests/basic/amps-and-angle-encoding.txt
-tests/basic/angle-links-and-img.html
-tests/basic/angle-links-and-img.txt
-tests/basic/auto-links.html
-tests/basic/auto-links.txt
-tests/basic/backlash-escapes.html
-tests/basic/backlash-escapes.txt
-tests/basic/blockquotes-with-code-blocks.html
-tests/basic/blockquotes-with-code-blocks.txt
-tests/basic/codeblock-in-list.html
-tests/basic/codeblock-in-list.txt
-tests/basic/hard-wrapped.html
-tests/basic/hard-wrapped.txt
-tests/basic/horizontal-rules.html
-tests/basic/horizontal-rules.txt
-tests/basic/inline-html-advanced.html
-tests/basic/inline-html-advanced.txt
-tests/basic/inline-html-comments.html
-tests/basic/inline-html-comments.txt
-tests/basic/inline-html-simple.html
-tests/basic/inline-html-simple.txt
-tests/basic/links-inline.html
-tests/basic/links-inline.txt
-tests/basic/links-reference.html
-tests/basic/links-reference.txt
-tests/basic/literal-quotes.html
-tests/basic/literal-quotes.txt
-tests/basic/markdown-documentation-basics.html
-tests/basic/markdown-documentation-basics.txt
-tests/basic/markdown-syntax.html
-tests/basic/markdown-syntax.txt
-tests/basic/nested-blockquotes.html
-tests/basic/nested-blockquotes.txt
-tests/basic/ordered-and-unordered-list.html
-tests/basic/ordered-and-unordered-list.txt
-tests/basic/strong-and-em-together.html
-tests/basic/strong-and-em-together.txt
-tests/basic/tabs.html
-tests/basic/tabs.txt
-tests/basic/tidyness.html
-tests/basic/tidyness.txt
-tests/extensions/admonition.html
-tests/extensions/admonition.txt
-tests/extensions/attr_list.html
-tests/extensions/attr_list.txt
-tests/extensions/codehilite.html
-tests/extensions/codehilite.txt
-tests/extensions/fenced_code.html
-tests/extensions/fenced_code.txt
-tests/extensions/github_flavored.html
-tests/extensions/github_flavored.txt
-tests/extensions/nl2br_w_attr_list.html
-tests/extensions/nl2br_w_attr_list.txt
-tests/extensions/sane_lists.html
-tests/extensions/sane_lists.txt
-tests/extensions/smarty.html
-tests/extensions/smarty.txt
-tests/extensions/toc.html
-tests/extensions/toc.txt
-tests/extensions/toc_invalid.html
-tests/extensions/toc_invalid.txt
-tests/extensions/toc_nested.html
-tests/extensions/toc_nested.txt
-tests/extensions/toc_nested2.html
-tests/extensions/toc_nested2.txt
-tests/extensions/toc_nested_list.html
-tests/extensions/toc_nested_list.txt
-tests/extensions/toc_out_of_order.html
-tests/extensions/toc_out_of_order.txt
-tests/extensions/wikilinks.html
-tests/extensions/wikilinks.txt
-tests/extensions/extra/abbr.html
-tests/extensions/extra/abbr.txt
-tests/extensions/extra/def-in-list.html
-tests/extensions/extra/def-in-list.txt
-tests/extensions/extra/extra_config.html
-tests/extensions/extra/extra_config.txt
-tests/extensions/extra/footnote.html
-tests/extensions/extra/footnote.txt
-tests/extensions/extra/footnote_many_footnotes.html
-tests/extensions/extra/footnote_many_footnotes.txt
-tests/extensions/extra/footnote_placeholder.html
-tests/extensions/extra/footnote_placeholder.txt
-tests/extensions/extra/footnote_placeholder_depth.html
-tests/extensions/extra/footnote_placeholder_depth.txt
-tests/extensions/extra/loose_def_list.html
-tests/extensions/extra/loose_def_list.txt
-tests/extensions/extra/markdown-syntax.html
-tests/extensions/extra/markdown-syntax.txt
-tests/extensions/extra/named_markers.html
-tests/extensions/extra/named_markers.txt
-tests/extensions/extra/raw-html.html
-tests/extensions/extra/raw-html.txt
-tests/extensions/extra/simple_def-lists.html
-tests/extensions/extra/simple_def-lists.txt
-tests/extensions/extra/tables.html
-tests/extensions/extra/tables.txt
-tests/extensions/extra/tables_and_attr_list.html
-tests/extensions/extra/tables_and_attr_list.txt
-tests/misc/CRLF_line_ends.html
-tests/misc/CRLF_line_ends.txt
-tests/misc/adjacent-headers.html
-tests/misc/adjacent-headers.txt
-tests/misc/ampersand.html
-tests/misc/ampersand.txt
-tests/misc/arabic.html
-tests/misc/arabic.txt
-tests/misc/autolinks_with_asterisks.html
-tests/misc/autolinks_with_asterisks.txt
-tests/misc/autolinks_with_asterisks_russian.html
-tests/misc/autolinks_with_asterisks_russian.txt
-tests/misc/backtick-escape.html
-tests/misc/backtick-escape.txt
-tests/misc/bidi.html
-tests/misc/bidi.txt
-tests/misc/blank-block-quote.html
-tests/misc/blank-block-quote.txt
-tests/misc/blank_lines_in_codeblocks.html
-tests/misc/blank_lines_in_codeblocks.txt
-tests/misc/block_html5.html
-tests/misc/block_html5.txt
-tests/misc/block_html_attr.html
-tests/misc/block_html_attr.txt
-tests/misc/block_html_simple.html
-tests/misc/block_html_simple.txt
-tests/misc/blockquote-below-paragraph.html
-tests/misc/blockquote-below-paragraph.txt
-tests/misc/blockquote-hr.html
-tests/misc/blockquote-hr.txt
-tests/misc/blockquote.html
-tests/misc/blockquote.txt
-tests/misc/bold_links.html
-tests/misc/bold_links.txt
-tests/misc/br.html
-tests/misc/br.txt
-tests/misc/bracket_re.html
-tests/misc/bracket_re.txt
-tests/misc/brackets-in-img-title.html
-tests/misc/brackets-in-img-title.txt
-tests/misc/code-first-line.html
-tests/misc/code-first-line.txt
-tests/misc/comments.html
-tests/misc/comments.txt
-tests/misc/div.html
-tests/misc/div.txt
-tests/misc/em-around-links.html
-tests/misc/em-around-links.txt
-tests/misc/em_strong.html
-tests/misc/em_strong.txt
-tests/misc/em_strong_complex.html
-tests/misc/em_strong_complex.txt
-tests/misc/email.html
-tests/misc/email.txt
-tests/misc/escaped_links.html
-tests/misc/escaped_links.txt
-tests/misc/funky-list.html
-tests/misc/funky-list.txt
-tests/misc/h1.html
-tests/misc/h1.txt
-tests/misc/hash.html
-tests/misc/hash.txt
-tests/misc/header-in-lists.html
-tests/misc/header-in-lists.txt
-tests/misc/headers.html
-tests/misc/headers.txt
-tests/misc/hline.html
-tests/misc/hline.txt
-tests/misc/html-comments.html
-tests/misc/html-comments.txt
-tests/misc/html.html
-tests/misc/html.txt
-tests/misc/image-2.html
-tests/misc/image-2.txt
-tests/misc/image_in_links.html
-tests/misc/image_in_links.txt
-tests/misc/ins-at-start-of-paragraph.html
-tests/misc/ins-at-start-of-paragraph.txt
-tests/misc/inside_html.html
-tests/misc/inside_html.txt
-tests/misc/japanese.html
-tests/misc/japanese.txt
-tests/misc/lazy-block-quote.html
-tests/misc/lazy-block-quote.txt
-tests/misc/link-with-parenthesis.html
-tests/misc/link-with-parenthesis.txt
-tests/misc/lists.html
-tests/misc/lists.txt
-tests/misc/lists2.html
-tests/misc/lists2.txt
-tests/misc/lists3.html
-tests/misc/lists3.txt
-tests/misc/lists4.html
-tests/misc/lists4.txt
-tests/misc/lists5.html
-tests/misc/lists5.txt
-tests/misc/lists6.html
-tests/misc/lists6.txt
-tests/misc/lists7.html
-tests/misc/lists7.txt
-tests/misc/lists8.html
-tests/misc/lists8.txt
-tests/misc/markup-inside-p.html
-tests/misc/markup-inside-p.txt
-tests/misc/mismatched-tags.html
-tests/misc/mismatched-tags.txt
-tests/misc/missing-link-def.html
-tests/misc/missing-link-def.txt
-tests/misc/more_comments.html
-tests/misc/more_comments.txt
-tests/misc/multi-line-tags.html
-tests/misc/multi-line-tags.txt
-tests/misc/multi-paragraph-block-quote.html
-tests/misc/multi-paragraph-block-quote.txt
-tests/misc/multi-test.html
-tests/misc/multi-test.txt
-tests/misc/multiline-comments.html
-tests/misc/multiline-comments.txt
-tests/misc/nested-lists.html
-tests/misc/nested-lists.txt
-tests/misc/nested-patterns.html
-tests/misc/nested-patterns.txt
-tests/misc/normalize.html
-tests/misc/normalize.txt
-tests/misc/numeric-entity.html
-tests/misc/numeric-entity.txt
-tests/misc/para-with-hr.html
-tests/misc/para-with-hr.txt
-tests/misc/php.html
-tests/misc/php.txt
-tests/misc/pre.html
-tests/misc/pre.txt
-tests/misc/raw_whitespace.html
-tests/misc/raw_whitespace.txt
-tests/misc/russian.html
-tests/misc/russian.txt
-tests/misc/smart_em.html
-tests/misc/smart_em.txt
-tests/misc/some-test.html
-tests/misc/some-test.txt
-tests/misc/span.html
-tests/misc/span.txt
-tests/misc/strong-with-underscores.html
-tests/misc/strong-with-underscores.txt
-tests/misc/stronintags.html
-tests/misc/stronintags.txt
-tests/misc/tabs-in-lists.html
-tests/misc/tabs-in-lists.txt
-tests/misc/two-spaces.html
-tests/misc/two-spaces.txt
-tests/misc/uche.html
-tests/misc/uche.txt
-tests/misc/underscores.html
-tests/misc/underscores.txt
-tests/misc/url_spaces.html
-tests/misc/url_spaces.txt
-tests/pl/Tests_2004/Amps and angle encoding.html
-tests/pl/Tests_2004/Auto links.html
-tests/pl/Tests_2004/Backslash escapes.html
-tests/pl/Tests_2004/Blockquotes with code blocks.html
-tests/pl/Tests_2004/Hard-wrapped paragraphs with list-like lines.html
-tests/pl/Tests_2004/Horizontal rules.html
-tests/pl/Tests_2004/Inline HTML (Advanced).html
-tests/pl/Tests_2004/Inline HTML (Simple).html
-tests/pl/Tests_2004/Inline HTML comments.html
-tests/pl/Tests_2004/Links, inline style.html
-tests/pl/Tests_2004/Links, reference style.html
-tests/pl/Tests_2004/Literal quotes in titles.html
-tests/pl/Tests_2004/Markdown Documentation - Basics.html
-tests/pl/Tests_2004/Markdown Documentation - Syntax.html
-tests/pl/Tests_2004/Nested blockquotes.html
-tests/pl/Tests_2004/Ordered and unordered lists.html
-tests/pl/Tests_2004/Strong and em together.html
-tests/pl/Tests_2004/Tabs.html
-tests/pl/Tests_2004/Tidyness.html
-tests/pl/Tests_2004/Yuri-Attributes.html
-tests/pl/Tests_2004/Yuri-Email.html
-tests/pl/Tests_2004/Yuri-Footnotes.html
-tests/pl/Tests_2004/Yuri-Links-in-Headers.html
-tests/pl/Tests_2007/Amps and angle encoding.html
-tests/pl/Tests_2007/Auto links.html
-tests/pl/Tests_2007/Backslash escapes.html
-tests/pl/Tests_2007/Blockquotes with code blocks.html
-tests/pl/Tests_2007/Code Blocks.html
-tests/pl/Tests_2007/Code Spans.html
-tests/pl/Tests_2007/Hard-wrapped paragraphs with list-like lines.html
-tests/pl/Tests_2007/Horizontal rules.html
-tests/pl/Tests_2007/Images.html
-tests/pl/Tests_2007/Inline HTML (Advanced).html
-tests/pl/Tests_2007/Inline HTML (Simple).html
-tests/pl/Tests_2007/Inline HTML comments.html
-tests/pl/Tests_2007/Links, inline style.html
-tests/pl/Tests_2007/Links, reference style.html
-tests/pl/Tests_2007/Links, shortcut references.html
-tests/pl/Tests_2007/Literal quotes in titles.html
-tests/pl/Tests_2007/Markdown Documentation - Basics.html
-tests/pl/Tests_2007/Markdown Documentation - Syntax.html
-tests/pl/Tests_2007/Nested blockquotes.html
-tests/pl/Tests_2007/Ordered and unordered lists.html
-tests/pl/Tests_2007/Strong and em together.html
-tests/pl/Tests_2007/Tabs.html
-tests/pl/Tests_2007/Tidyness.html
-tests/test_syntax/__init__.py
-tests/test_syntax/blocks/__init__.py
-tests/test_syntax/blocks/test_code_blocks.py
-tests/test_syntax/blocks/test_headers.py
-tests/test_syntax/blocks/test_hr.py
-tests/test_syntax/blocks/test_paragraphs.py
-tests/test_syntax/extensions/__init__.py
-tests/test_syntax/extensions/test_fenced_code.py
-tests/test_syntax/extensions/test_footnotes.py
-tests/test_syntax/extensions/test_legacy_attrs.py
-tests/test_syntax/extensions/test_legacy_em.py
-tests/test_syntax/extensions/test_tables.py
-tests/test_syntax/extensions/test_toc.py
-tests/test_syntax/inline/__init__.py
-tests/test_syntax/inline/test_emphasis.py
-tests/test_syntax/inline/test_entities.py
-tests/test_syntax/inline/test_images.py
-tests/test_syntax/inline/test_links.py
-tests/test_syntax/inline/test_raw_html.py
\ No newline at end of file
+++ /dev/null
-[console_scripts]
-markdown_py = markdown.__main__:run
-
-[markdown.extensions]
-abbr = markdown.extensions.abbr:AbbrExtension
-admonition = markdown.extensions.admonition:AdmonitionExtension
-attr_list = markdown.extensions.attr_list:AttrListExtension
-codehilite = markdown.extensions.codehilite:CodeHiliteExtension
-def_list = markdown.extensions.def_list:DefListExtension
-extra = markdown.extensions.extra:ExtraExtension
-fenced_code = markdown.extensions.fenced_code:FencedCodeExtension
-footnotes = markdown.extensions.footnotes:FootnoteExtension
-legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension
-legacy_em = markdown.extensions.legacy_em:LegacyEmExtension
-md_in_html = markdown.extensions.md_in_html:MarkdownInHtmlExtension
-meta = markdown.extensions.meta:MetaExtension
-nl2br = markdown.extensions.nl2br:Nl2BrExtension
-sane_lists = markdown.extensions.sane_lists:SaneListExtension
-smarty = markdown.extensions.smarty:SmartyExtension
-tables = markdown.extensions.tables:TableExtension
-toc = markdown.extensions.toc:TocExtension
-wikilinks = markdown.extensions.wikilinks:WikiLinkExtension
-
+++ /dev/null
-
-[:python_version < "3.8"]
-importlib_metadata
-
-[testing]
-coverage
-pyyaml
+++ /dev/null
-Metadata-Version: 2.1
-Name: Markdown
-Version: 3.2.2
-Summary: Python implementation of Markdown.
-Home-page: https://Python-Markdown.github.io/
-Author: Manfred Stienstra, Yuri takhteyev and Waylan limberg
-Author-email: waylan.limberg@icloud.com
-Maintainer: Waylan Limberg
-Maintainer-email: waylan.limberg@icloud.com
-License: BSD License
-Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-3.2.2-py2.py3-none-any.whl
-Description:
- This is a Python implementation of John Gruber's Markdown_.
- It is almost completely compliant with the reference implementation,
- though there are a few known issues. See Features_ for information
- on what exactly is supported and what is not. Additional features are
- supported by the `Available Extensions`_.
-
- .. _Markdown: https://daringfireball.net/projects/markdown/
- .. _Features: https://Python-Markdown.github.io#features
- .. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
-
- Support
- =======
-
- You may report bugs, ask for help, and discuss various other issues on
- the `bug tracker`_.
-
- .. _`bug tracker`: https://github.com/Python-Markdown/markdown/issues
-
-Platform: UNKNOWN
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: License :: OSI Approved :: BSD License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.5
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: Python :: Implementation :: PyPy
-Classifier: Topic :: Communications :: Email :: Filters
-Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries
-Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
-Classifier: Topic :: Software Development :: Documentation
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Topic :: Text Processing :: Filters
-Classifier: Topic :: Text Processing :: Markup :: HTML
-Requires-Python: >=3.5
-Provides-Extra: testing
[Python-Markdown][]
===================
-[![Build Status][travis-button]][travis]
+[![Build Status][build-button]][build]
[![Coverage Status][codecov-button]][codecov]
[![Latest Version][mdversion-button]][md-pypi]
[![Python Versions][pyversion-button]][md-pypi]
[![BSD License][bsdlicense-button]][bsdlicense]
[![Code of Conduct][codeofconduct-button]][Code of Conduct]
-[travis-button]: https://img.shields.io/travis/Python-Markdown/markdown.svg
-[travis]: https://travis-ci.org/Python-Markdown/markdown
+[build-button]: https://github.com/Python-Markdown/markdown/workflows/CI/badge.svg?event=push
+[build]: https://github.com/Python-Markdown/markdown/actions?query=workflow%3ACI+event%3Apush
[codecov-button]: https://codecov.io/gh/Python-Markdown/markdown/branch/master/graph/badge.svg
[codecov]: https://codecov.io/gh/Python-Markdown/markdown
[mdversion-button]: https://img.shields.io/pypi/v/Markdown.svg
--- /dev/null
+#!/bin/bash
+
+echo "Checking links in documentation..."
+
+# List of files in docs dir
+docs=$(find . -path './docs/*.md')
+# List of files in project root (README, etc)
+extras=$(find . -maxdepth 1 -name '*.md')
+# Combined list of files to check
+files=("${docs[@]}" "${extras[@]}")
+
+let "fails=0"
+let "count=0"
+
+for file in ${files[@]}; do
+ let "count++"
+ markdown-link-check -q "$file"
+ if [ $? -ne 0 ]; then
+ let "fails++"
+ fi
+done
+
+echo -e "\n\033[0;33m$count files checked."
+
+if [ $fails -gt 0 ]; then
+ echo -e "\033[0;31mERROR: $fails files with dead links found!"
+ exit 1
+else
+ echo -e "\033[0;32mCongratulations! No dead links found."
+ exit 0
+fi
--- /dev/null
+#!/bin/bash
+
+echo "Building docs..."
+mkdocs build --strict
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+echo "Compiling Dictionary..."
+aspell --lang=en create master ./tmp <.spell-dict
+if [ $? -ne 0 ]; then
+ exit 1
+fi
+echo "Checking spelling..."
+
+let "fails=0"
+
+for file in $(find site/ -type f -name "*.html"); do
+ words=$(aspell list --lang=en --mode=html --add-html-skip=code --extra-dicts=./tmp <$file)
+ if [ "$words" ]; then
+ uniquewords=$(tr ' ' '\n' <<< "${words[@]}" | sort -u | tr '\n' ' ')
+ let "fails++"
+ echo "Misspelled words in '$file':"
+ echo "-----------------------------------------------------------------"
+ for word in ${uniquewords[@]}; do
+ echo $word
+ done
+ echo "-----------------------------------------------------------------"
+ fi
+done
+rm -f ./tmp
+rm -rf site
+
+if [ $fails -gt 0 ]; then
+ echo "$fails files with misspelled words."
+ exit 1
+else
+ exit 0
+fi
Python-Markdown Change Log
=========================
+Oct 6, 2020: version 3.3 ([Notes](release-3.3.md)).
+
May 8, 2020: version 3.2.2 (a bug-fix release).
* Add `checklinks` tox environment to ensure all links in documentation are good.
* Do not double escape entities in TOC.
* Correctly report if an extension raises a `TypeError` (#939).
* Raise a `KeyError` when attempting to delete a nonexistent key from the
- extension registry (#939).
+ extension registry (#939).
* Remove import of `packaging` (or `pkg_resources` fallback) entirely.
* Remove `setuptools` as a run-time dependency (`install_required`).
--- /dev/null
+title: Release Notes for v3.3
+
+# Python-Markdown 3.3 Release Notes
+
+Python-Markdown version 3.3 supports Python versions 3.6, 3.7, 3.8, 3.9 and PyPy3.
+
+## Backwards-incompatible changes
+
+### The prefix `language-` is now prepended to all language classes by default on code blocks.
+
+The [HTML5 spec][spec] recommends that the class defining the language of a code block be prefixed with `language-`.
+Therefore, by default, both the [fenced_code] and [codehilite] extensions now prepend the prefix when code
+highlighting is disabled.
+
+If you have previously been including the prefix manually in your fenced code blocks, then you will not want a second
+instance of the prefix. Similarly, if you are using a third party syntax highlighting tool which does not recognize
+the prefix, or requires a different prefix, then you will want to redefine the prefix globally using the `lang_prefix`
+configuration option of either the `fenced_code` or `codehilite` extensions.
+
+For example, to configure `fenced_code` to not apply any prefix (the previous behavior), set the option to an empty string:
+
+```python
+from markdown.extensions.fenced_code import FencedCodeExtension
+
+markdown.markdown(src, extensions=[FencedCodeExtension(lang_prefix='')])
+```
+
+!!! note
+ When code highlighting is [enabled], the output from Pygments is used unaltered. Currently, Pygments does not
+ provide an option to include the language class in the output, let alone prefix it. Therefore, any language prefix
+ is only applied when syntax highlighting is disabled.
+
+### Attribute Lists are more strict (#898).
+
+Empty curly braces are now completely ignored by the [Attribute List] extension. Previously, the extension would
+recognize them as attribute lists and remove them from the document. Therefore, it is no longer necessary to backslash
+escape a set of curly braces which are empty or only contain whitespace.
+
+Despite not being documented, previously an attribute list could be defined anywhere within a table cell and get
+applied to the cell (`<td>` element). Now the attribute list must be defined at the end of the cell content and must
+be separated from the rest of the content by at least one space. This makes it easy to differentiate between attribute
+lists defined on inline elements within a cell and the attribute list for the cell itself. It is also more consistent
+with how attribute lists are defined on other types of elements.
+
+The extension has also added support for defining attribute lists on table header cells (`<th>` elements) in the same
+manner as data cells (`<td>` elements).
+
+In addition, the documentation for the extensions received an overhaul. The features (#987) and limitations (#965) of the extension are now fully documented.
+
+## New features
+
+The following new features have been included in the 3.3 release:
+
+* All Pygments' options are now available for syntax highlighting (#816).
+ - The [Codehilite](../extensions/code_hilite.md) extension now accepts any options
+ which Pygments supports as global configuration settings on the extension.
+ - [Fenced Code Blocks](../extensions/fenced_code_blocks.md) will accept any of the
+ same options on individual code blocks.
+ - Any of the previously supported aliases to Pygments' options continue to be
+ supported at this time. However, it is recommended that the Pygments option names
+ be used directly to ensure continued compatibility in the future.
+
+* [Fenced Code Blocks](../extensions/fenced_code_blocks.md) now work with
+ [Attribute Lists](../extensions/attr_list.md) when syntax highlighting is disabled.
+ Any random HTML attribute can be defined and set on the `<code>` tag of fenced code
+ blocks when the `attr_list` extension is enabled (#816).
+
+* The HTML parser has been completely replaced. The new HTML parser is built on Python's
+ [html.parser.HTMLParser](https://docs.python.org/3/library/html.parser.html), which
+ alleviates various bugs and simplify maintenance of the code (#803, #830).
+
+* The [Markdown in HTML](../extensions/md_in_html.md) extension has been rebuilt on the
+ new HTML Parser, which drastically simplifies it. Note that raw HTML elements with a
+ `markdown` attribute defined are now converted to ElementTree Elements and are rendered
+ by the serializer. Various bugs have been fixed (#803, #595, #780, and #1012).
+
+* Link reference parsing, abbreviation reference parsing and footnote reference parsing
+ has all been moved from `preprocessors` to `blockprocessors`, which allows them to be
+ nested within other block level elements. Specifically, this change was necessary to
+ maintain the current behavior in the rebuilt Markdown in HTML extension. A few random
+ edge-case bugs (see the included tests) were resolved in the process (#803).
+
+* An alternate function `markdown.extensions.headerid.slugify_unicode` has been included
+ with the [Table of Contents](../extensions/toc.md) extension which supports Unicode
+ characters in table of contents slugs. The old `markdown.extensions.headerid.slugify`
+ method which removes non-ASCII characters remains the default. Import and pass
+ `markdown.extensions.headerid.slugify_unicode` to the `slugify` configuration option
+ to use the new behavior.
+
+* Support was added for Python 3.9 and dropped for Python 3.5.
+
+## Bug fixes
+
+The following bug fixes are included in the 3.3 release:
+
+* Document how to pass configuration options to Extra (#1019).
+* Fix HR which follows strong em (#897).
+* Support short reference image links (#894).
+* Avoid a `RecursionError` from deeply nested blockquotes (#799).
+* Fix issues with complex emphasis (#979).
+* Fix unescaping of HTML characters `<>` in CodeHilite (#990).
+* Fix complex scenarios involving lists and admonitions (#1004).
+* Fix complex scenarios with nested ordered and unordered lists in a definition list (#918).
+
+[spec]: https://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
+[fenced_code]: ../extensions/fenced_code_blocks.md
+[codehilite]: ../extensions/code_hilite.md
+[enabled]: ../extensions/fenced_code_blocks.md#enabling-syntax-highlighting
+[Attribute List]: ../extensions/attr_list.md
Pull requests will generally not be accepted if any tests are failing.
Therefore, it is recommended that you run the tests before submitting your pull
-request. After making a pull request, check the Travis build status in the
+request. After making a pull request, check the build status in the
GitHub interface to ensure that all tests are running as expected. If any checks
fail, you may push additional commits to your branch. GitHub will add those
commits to the pull request and rerun the checks.
Bump version to X.X.X
-6. After all checks (Travis, etc.) have passed, merge the pull request.
+6. After all checks have passed, merge the pull request.
7. Create a git tag with the new version as the tag name and push to the
- [Python-Markdown/markdown] repository.
+ [Python-Markdown/markdown] repository. The new tag should trigger a GitHub
+ workflow which will automatically deploy the release to PyPI and update the
+ documentation.
-8. Deploy the release to [PyPI] with the command `make deploy`.
+ In the event that the deployment fails, the following steps can be taken to
+ deploy manually:
-9. Deploy an update to the documentation using [MkDocs]. The following example
- assumes that local clones of the [Python-Markdown/markdown] and
- [Python-Markdown/Python-Markdown.github.io] repositories are in sibling
- directories named `markdown` and `Python-Markdown.github.io` respectively.
+ - Deploy the release to [PyPI] with the command `make deploy`.
- cd Python-Markdown.github.io
- mkdocs gh-deploy --config-file ../markdown/mkdocs.yml --remote-branch master
+ - Deploy an update to the documentation using [MkDocs]. The following example
+ assumes that local clones of the [Python-Markdown/markdown] and
+ [Python-Markdown/Python-Markdown.github.io] repositories are in sibling
+ directories named `markdown` and `Python-Markdown.github.io` respectively.
+
+ cd Python-Markdown.github.io
+ mkdocs gh-deploy --config-file ../markdown/mkdocs.yml --remote-branch master
## Issue and Pull Request Labels
id="id2" class="class2 class3 class4"
```
+HTML includes support for some attributes to be a single term, like `checked`, for example. Therefore, the attribute
+list `{: checked }` would result in `checked` if the [output format](../reference.md#output_format) is `html` or
+`checked="checked"` if the output format is `xhtml`.
+
+Curly braces can be backslash escaped to avoid being identified as an attribute list.
+
+```text
+\{ not an attribute list }
+```
+
+Opening and closing curly braces which are empty or only contain whitespace are ignored whether they are escaped or
+not. Additionally, any attribute lists which are not located in the specific locations documented below are ignored.
+
+The colon after the opening brace is optional, but is supported to maintain consistency with other implementations.
+Therefore, the following is also a valid attribute list:
+
+```text
+{ #someid .someclass somekey='some value' }
+```
+
+In addition, the spaces after the opening brace and before the closing brace are optional. They are recommended as
+they improve readability, but they are not required.
+
+The Attribute List extension does not have any knowledge of which keys and/or values are valid in HTML. Therefore, it
+is up to the document author to ensure that valid keys and values are used. However, the extension will escape any
+characters in the key which are not valid by replacing them with an underscore. Multiple consecutive invalid
+characters are reduced to a single underscore.
+
### Block Level
To define attributes for a block level element, the attribute list should
<p id="an_id" class="a_class">This is a paragraph.</p>
```
-The one exception is headers, as they are only ever allowed on one line.
+An exception is headers, as they are only ever allowed on one line.
```text
A setext style header {: #setext}
<h3 id="hash">A hash style header</h3>
```
+!!! seealso "See Also"
+ By default, the [Fenced Code Blocks](./fenced_code_blocks.md#attributes) extension includes limited support for
+ attribute lists. To get [full support](./fenced_code_blocks.md#keyvalue-pairs), both extensions must be enabled.
+
### Inline
To define attributes on inline elements, the attribute list should be defined
<p><a href="http://example.com" class="foo bar" title="Some title!">link</a></p>
```
+If the [tables](./tables.md) extension is enabled, attribute lists can be defined on table cells. To differentiate
+attributes for an inline element from attributes for the containing cell, the attribute list must be separated from
+the content by at least one space and be defined at the end of the cell content. As table cells can only ever be on
+a single line, the attribute list must remain on the same line as the content of the cell.
+
+```text
+| set on td | set on em |
+|--------------|-------------|
+| *a* { .foo } | *b*{ .foo } |
+```
+
+The above example results in the following output:
+
+```html
+<table>
+ <thead>
+ <tr>
+ <th>set on td</th>
+ <th>set on em</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="foo"><em>a</em></td>
+ <td><em class="foo">b</em></td>
+ </tr>
+ </tbody>
+</table>
+```
+
+Note that in the first column, the attribute list is preceded by a space; therefore, it is assigned to the table cell
+(`<td>` element). However, in the second column, the attribute list is not preceded by a space; therefore, it is
+assigned to the inline element (`<em>`) which immediately preceded it.
+
+Attribute lists may also be defined on table header cells (`<th>` elements) in the same manner.
+
+### Limitations
+
+There are a few types of elements which attribute lists do not work with. As a reminder, Markdown is a subset of HTML
+and anything which cannot be expressed in Markdown can always be expressed with raw HTML directly.
+
+__Code Blocks:__
+
+: Code blocks are unique in that they must be able to display Markdown syntax. Therefore, there is no way to
+ determine if an attribute list is intended to be part of the code block or intended to define attributes on the
+ wrapping element. For that reason, the extension ignores code blocks. To define attributes on code blocks, the
+ [codehilite] and [fenced code blocks] extensions provide some options.
+
+[codehilite]: code_hilite.md
+[fenced code blocks]: fenced_code_blocks.md
+
+__Nested Elements:__
+
+: Markdown provides mechanisms for nesting various block level elements within other elements. However, attribute
+ lists only ever get applied to the immediate parent. There is no way to specify that an attribute list should be
+ applied some number of levels up the document tree. For example, when including an attribute list within a
+ blockquote, the attribute list is only ever applied to the paragraph the list is defined in. There is no way to
+ define attributes on the `blockquote` element itself.
+
+__Implied Elements:__
+
+: There are various HTML elements which are not represented in Markdown text, but only implied. For example, the
+ `ul` and `ol` elements do not exist in Markdown. They are only implied by the presence of list items (`li`). There
+ is no way to use an attribute list to define attributes on implied elements, including but not limited to the
+ following: `ul`, `ol`, `dl`, `table`, `thead`, `tbody`, and `tr`.
+
## Usage
See [Extensions](index.md) for general extension usage. Use `attr_list` as the
defined, Pygments will attempt to guess the language. When that fails, the code
block will not be highlighted.
-!!! note "See Also"
+!!! seealso "See Also"
GitHub user [richeland] has provided a number of different [CSS style
sheets][rich] which work with Pygments along with a [preview] of each theme.
- The `css_class` used is the same as the default value for that option
- (`.codehilite`). However, the Python-Markdown project makes no guarantee that
- richeland's CSS styles will work with the version of Pygments you are using.
- To ensure complete compatibility, you should generate the CSS rules from
- your own installation of Pygments.
+ The `css_class` used is `.highlight`. Therefore, one would need to override the
+ [`css_class`](#css_class) option when using richeland's CSS styles. However, the
+ Python-Markdown project makes no guarantee that richeland's CSS styles will
+ work with the version of Pygments you are using. To ensure complete
+ compatibility, you should generate the CSS rules from your own installation
+ of Pygments.
[richeland]: https://github.com/richleland
[rich]: https://github.com/richleland/pygments-css
The following options are provided to configure the output:
-* **`linenums`**:
- Use line numbers. Possible values are `True` for yes, `False` for no and
- `None` for auto. Defaults to `None`.
+* **`linenums`**{ #linenums }:
+ An alias to Pygments' `linenos` formatter option. Possible values are `True` for yes, `False` for no and `None`
+ for auto. Defaults to `None`.
Using `True` will force every code block to have line numbers, even when
using colons (`:::`) for language identification.
Using `False` will turn off all line numbers, even when using shebangs
(`#!`) for language identification.
-* **`guess_lang`**:
+* **`guess_lang`**{ #guess_lang }:
Automatic language detection. Defaults to `True`.
Using `False` will prevent Pygments from guessing the language, and thus
highlighting blocks only when you explicitly set the language.
-* **`css_class`**:
- Set CSS class name for the wrapper `<div>` tag. Defaults to
+* **`css_class`**{ #css_class }:
+ An alias to Pygments `cssclass` formatter option. Set CSS class name for the wrapper `<div>` tag. Defaults to
`codehilite`.
-* **`pygments_style`**:
+* **`pygments_style`**{ #pygments_style }:
Pygments HTML Formatter Style (`ColorScheme`). Defaults to `default`.
!!! Note
This is useful only when `noclasses` is set to `True`, otherwise the
CSS styles must be provided by the end user.
-* **`noclasses`**:
+* **`noclasses`**{ #noclasses }:
Use inline styles instead of CSS classes. Defaults to `False`.
-* **`use_pygments`**:
+* **`use_pygments`**{ #use_pygments }:
Specifies the use of Pygments in generating the output.
If `True` (the default) and Pygments is available, CodeHilite will use
>= 2.4, the output will be wrapped in `<code>` tags, whereas earlier
versions will not.
- Otherwise, Pygments will not be used. If a language is defined for a code
- block, it will be assigned to the `<code>` tag as a class in the manner
- suggested by the [HTML5 spec][spec] (alternate output will not be
- entertained) and may be used by a JavaScript library in the browser to
- highlight the code block.
-
+ Otherwise, Pygments will not be used. If a language is defined for a code block, it will be assigned to the
+ `<code>` tag as a class in the manner suggested by the [HTML5 spec][spec] and may be used by a JavaScript library
+ in the browser to highlight the code block. See the [`lang_prefix`](#lang_prefix) option to customize the prefix.
+
+* **`lang_prefix`**{ #lang_prefix }:
+ The prefix prepended to the language class assigned to the HTML `<code>` tag. Default: `language-`.
+
+ This option only applies when `use_pygments` is `False` as Pygments does not provide an option to include a
+ language prefix.
+
+
+* Any other Pygments' options:
+
+ All other options are accepted and passed on to Pygments' lexer and formatter. Therefore,
+ valid options include any options which are accepted by the [html formatter] or
+ whichever [lexer] the code's language uses. Invalid options are ignored without error.
+
A trivial example:
```python
markdown.markdown(some_text, extensions=['codehilite'])
```
+[html formatter]: https://pygments.org/docs/formatters/#HtmlFormatter
+[lexer]: https://pygments.org/docs/lexers/
[spec]: https://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
>>> html = markdown.markdown(text, extensions=['extra'])
```
-There may be [additional extensions](index.md) that are distributed with
-Python-Markdown that are not included here in Extra. The features
-of those extensions are not part of PHP Markdown Extra, and
-therefore, not part of Python-Markdown Extra. If you really would
-like Extra to include additional extensions, we suggest creating
-your own clone of Extra under a different name
-(see the [Extension API](api.md)).
+To pass configuration options to the extensions included with Extra, they must be passed to Extra, with the
+underlying extension identified as well. In that way Extra will have access to the options and can pass them on to
+the appropriate underlying extension.
+
+```python
+config = {
+ 'extra': {
+ 'footnotes': {
+ 'UNIQUE_IDS': True
+ },
+ 'fenced_code': {
+ 'lang_prefix': 'lang-'
+ }
+ },
+ 'toc': {
+ 'permalink': True
+ }
+}
+
+html = markdown.markdown(text, extensions=['extra', 'toc'], extension_configs=config)
+```
+
+Note that in the above example, `footnotes` and `fenced_code` are both nested under the `extra` key as those
+extensions are included with Extra. However, the `toc` extension is not included with `extra` and therefore its
+configuration options are not nested under the `extra` key.
+
+See each individual extension for a list of supported configuration options.
+
+There are many other [extensions](index.md) which are distributed with Python-Markdown that are not included here in
+Extra. The features of those extensions are not part of PHP Markdown Extra, and therefore, not part of Python-Markdown
+Extra.
## Summary
-The Fenced Code Blocks extension adds a secondary way to define code blocks,
-which overcomes a few limitations of the indented code blocks.
+The Fenced Code Blocks extension adds a secondary way to define code blocks, which overcomes a few limitations of
+indented code blocks.
This extension is included in the standard Markdown library.
## Syntax
-Fenced Code Blocks are defined using the syntax established in
-[PHP Markdown Extra][php].
+Fenced Code Blocks are defined using the syntax originally established in [PHP Markdown Extra][php] and popularized by
+[GitHub Flavored Markdown][gfm].
-[php]: http://www.michelf.com/projects/php-markdown/extra/#fenced-code-blocks
+Fenced code blocks begin with three or more backticks (` ``` `) or tildes (`~~~`) on a line by themselves and end with
+a matching set of backticks or tildes on a line by themselves. The closing set must contain the same number and type
+of characters as the opening set. It is recommended that a blank line be placed before and after the code block.
+
+````md
+A paragraph before the code block.
+
+```
+a one-line code block
+```
-Thus, the following text (taken from the above referenced PHP documentation):
+A paragraph after the code block.
+````
-```md
-This is a paragraph introducing:
+While backticks seem to be more popular among users, tildes may be used as well.
-~~~~~~~~~~~~~~~~~~~~~
+````md
+~~~
a one-line code block
-~~~~~~~~~~~~~~~~~~~~~
+~~~
+````
+
+To include a set of backticks (or tildes) within a code block, use a different number of backticks for the
+deliminators.
+
+`````md
+````
+```
+````
+`````
+
+Fenced code blocks can have a blank line as the first and/or last line of the code block and those lines will be
+preserved.
+
+````md
```
-Fenced code blocks can have a blank line as the first and/or last line of a
-code block and they can also come immediately after a list item without becoming
+a three-line code block
+
+```
+````
+
+Unlike indented code blocks, a fenced code block can immediately follow a list item without becoming
part of the list.
+````md
+* A list item.
+
+```
+not part of the list
+```
+````
+
!!! warning
- Fenced Code Blocks are only supported at the document root level.
- Therefore, they cannot be nested inside lists or blockquotes.
- If you need to nest fenced code blocks, you may want to try the
- the third party extension [SuperFences] instead.
+ Fenced Code Blocks are only supported at the document root level. Therefore, they cannot be nested inside lists or
+ blockquotes. If you need to nest fenced code blocks, you may want to try the third party extension [SuperFences]
+ instead.
-[SuperFences]: https://facelessuser.github.io/pymdown-extensions/extensions/superfences/
+### Attributes
+
+Various attributes may be defined on a per-code-block basis by defining them immediately following the opening
+deliminator. The attributes should be wrapped in curly braces `{}` and be on the same line as the deliminator. It is
+generally best to separate the attribute list from the deliminator with a space. Attributes within the list must be
+separated by a space.
+
+````md
+``` { attributes go here }
+a code block with attributes
+```
+````
-### Language
+How those attributes will affect the output will depend on various factors as described below.
-In addition to PHP Extra's syntax, you can define the language of the code
-block for use by syntax highlighters etc. The language will be assigned as a
-class attribute of the ``<code>`` element in the output. Therefore, you should
-define the language as you would a CSS class - ``.language``. For consistency
-with other markdown syntax, the language can *optionally* be wrapped in curly
-brackets:
+#### Language
-```md
-~~~~{.python}
-# python code
-~~~~
+The language of the code within a code block can be specified for use by syntax highlighters, etc. The language should
+be prefixed with a dot and not contain any whitespace (`.language-name`).
-~~~~.html
+````md
+``` { .html }
<p>HTML Document</p>
-~~~~
```
+````
-The above will output:
+So long as the language is the only option specified, the curly brackets and/or the dot may be excluded:
+
+````md
+``` html
+<p>HTML Document</p>
+```
+````
+
+Either of the above examples will output the following HTML:
```html
-<pre><code class="python"># python code
+<pre><code class="language-html"><p>HTML Document</p>
</code></pre>
+```
+
+Note that the language name has been prefixed with `language-` and it has been assigned to the `class` attribute on
+the `<code>` tag, which is the format suggested by the [HTML 5 Specification][html5] (see the second "example" in the
+Specification). While `language` is the default prefix, the prefix may be overridden using the
+[`lang_prefix`](#lang_prefix) configuration option.
+
+#### Classes
-<pre><code class="html"><p>HTML Document</p>
+In addition to the language, additional classes may be defined by prefixing them with a dot, just like the language.
+
+````md
+``` { .html .foo .bar }
+<p>HTML Document</p>
+```
+````
+
+When defining multiple classes, only the first class will be used as the "language" for the code block. All others are
+assigned to the `<code>` tag unaltered. Additionally, the curly braces and dot are required for all classes, including
+the language class if more than one class is defined.
+
+The above example will output the following HTML:
+
+```html
+<pre><code class="language-html foo bar"><p>HTML Document</p>
</code></pre>
```
-[GitHub][]'s backtick (`\``) syntax is also supported in this extension:
+#### ID
+
+An `id` can be defined for a code block, which would allow a link to point directly to the code block using a URL
+hash. IDs must be prefixed with a hash character (`#`) and only contain characters permitted in HTML `id` attributes.
````md
-```python
-# more python code
+``` { #example }
+A linkable code block
```
````
-[GitHub]: https://github.github.com/github-flavored-markdown/
+The `id` attribute is assigned to the `<pre>` tag of the output. The above example will output the following HTML:
-### Emphasized Lines
+```html
+<pre id="example"><code>A linkable code block
+</code></pre>
+```
-If you would like to have your fenced code blocks highlighted with the
-[CodeHilite][] extension, simply enable that extension (remember that
-[Pygments][] is its dependency) and the language of your fenced code blocks
-will be passed in and highlighted appropriately.
+From elsewhere within the same document, one could link to the code block with `[link](#example)`.
-Similar to the [colon][] syntax of the CodeHilite extension, fenced code blocks
-can also have emphasized certain lines of code.
+IDs may be defined along with the language, other classes, or any other supported attributes. The order of items does
+not matter.
+
+````md
+``` { #example .lang .foo .bar }
+A linkable code block
+```
+````
+
+#### Key/Value Pairs
+
+If the `fenced_code` and [`attr_list`][attr_list] extensions are both enabled, then key/value pairs can be defined in
+the attribute list. So long as code highlighting is not enabled (see below), the key/value pairs will be assigned as
+attributes on the `<code>` tag in the output. Key/value pairs must be defined using the syntax documented for the
+`attr_list` extension (for example, values with whitespace must be wrapped in quotes).
+
+````md
+``` { .lang #example style="color: #333; background: #f8f8f8;" }
+A code block with inline styles. Fancy!
+```
+````
-The lines can be specified with PHP Extra's syntax:
+The above example will output the following HTML:
-```md
-~~~~{.python hl_lines="1 3"}
-# This line is emphasized
-# This line isn't
-# This line is emphasized
-~~~~
+```html
+<pre id="example"><code class="language-lang" style="color: #333; background: #f8f8f8;">A code block with inline styles. Fancy!
+</code></pre>
```
-... or with GitHub's:
+If the `attr_list` extension is not enabled, then the key/value pairs will be ignored.
+
+#### Syntax Highlighting
+
+If the `fenced_code` extension and syntax highlighting are both enabled, then the [`codehilite`][codehilite]
+extension will be used for syntax highlighting the contents of the code block. The language defined in the attribute
+list will be passed to `codehilite` to ensure that the correct language is used. If no language is specified and
+language guessing is not disabled for the `codehilite` extension, then the language will be guessed.
+
+The `codehilite` extension uses the [Pygments] engine to do syntax highlighting. Any valid Pygments options can be
+defined as key/value pairs in the attribute list and will be passed on to Pygments.
````md
-```python hl_lines="1 3"
-# This line is emphasized
-# This line isn't
-# This line is emphasized
+``` { .lang linenos=true linenostart=42 hl_lines="43-44 50" title="An Example Code Block" }`
+A truncated code block...
```
````
-[CodeHilite]: code_hilite.md
-[Pygments]: http://pygments.org/
-[colon]: code_hilite.md#colons
+Valid options include any option accepted by Pygments' [`HTMLFormatter`][HTMLFormatter] except for the `full` option,
+as well as any options accepted by the relevant [lexer][lexer] (each language has its own lexer). While most lexers
+don't have options that are all that useful in this context, there are a few important exceptions. For example, the
+[PHP lexer's] `startinline` option eliminates the need to start each code fragment with `<?php`.
+
+!!! note
+
+ The `fenced_code` extension does not alter the output provided by Pygments. Therefore, only options which Pygments
+ provides can be utilized. As Pygments does not currently provide a way to define an ID, any ID defined in an
+ attribute list will be ignored when syntax highlighting is enabled. Additionally, any key/value pairs which are not Pygments options will be ignored, regardless of whether the `attr_list` extension is enabled.
+
+##### Enabling Syntax Highlighting
+
+To enable syntax highlighting, the [`codehilite`][codehilite] extension must be enabled and the `codehilite`
+extension's `use_pygments` option must be set to `True` (the default).
+
+Alternatively, so long as the `codehilite` extension is enabled, you can override a global `use_pygments=False`
+option for an individual code block by including `use_pygments=true` in the attribute list. While the `use_pygments`
+key/value pair will not be included in the output, all other attributes will behave as they would if syntax
+highlighting was enabled only for that code block.
+
+Conversely, to disable syntax highlighting on an individual code block, include `use_pygments=false` in the attribute
+list. While the `use_pygments` key/value pair will not be included in the output, all other attributes will behave as
+they would if syntax highlighting was disabled for that code block regardless of any global setting.
+
+!!! seealso "See Also"
+
+ You will need to properly install and setup Pygments for syntax highlighting to work. See the `codehilite`
+ extension's [documentation][setup] for details.
## Usage
-See [Extensions](index.md) for general extension usage. Use `fenced_code` as
-the name of the extension.
+See [Extensions] for general extension usage. Use `fenced_code` as the name of the extension.
+
+See the [Library Reference] for information about configuring extensions.
-This extension does not accept any special configuration options.
+The following option is provided to configure the output:
+
+* **`lang_prefix`**{#lang_prefix}:
+ The prefix prepended to the language class assigned to the HTML `<code>` tag. Default: `language-`.
A trivial example:
```python
markdown.markdown(some_text, extensions=['fenced_code'])
```
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#fenced-code-blocks
+[gfm]: https://help.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks
+[SuperFences]: https://facelessuser.github.io/pymdown-extensions/extensions/superfences/
+[html5]: https://html.spec.whatwg.org/#the-code-element
+[attr_list]: ./attr_list.md
+[codehilite]: ./code_hilite.md
+[Pygments]: http://pygments.org/
+[HTMLFormatter]: https://pygments.org/docs/formatters/#HtmlFormatter
+[lexer]: https://pygments.org/docs/lexers/
+[PHP lexer's]: https://pygments.org/docs/lexers/#lexers-for-php-and-related-languages
+[setup]: ./code_hilite.md#setup
+[Extensions]: ./index.md
+[Library Reference]: ../reference.md#extensions
## Summary
-An extensions that parses Markdown inside of HTML tags.
+An extension that parses Markdown inside of HTML tags.
-## Usage
+## Syntax
-From the Python interpreter:
+By default, Markdown ignores any content within a raw HTML block-level element. With the `md-in-html` extension
+enabled, the content of a raw HTML block-level element can be parsed as Markdown by including a `markdown` attribute
+on the opening tag. The `markdown` attribute will be stripped from the output, while all other attributes will be
+preserved.
-```pycon
->>> import markdown
->>> html = markdown.markdown(text, extensions=['md_in_html'])
-```
+The `markdown` attribute can be assigned one of three values: [`"1"`](#1), [`"block"`](#block), or [`"span"`](#span).
-Unlike the other Extra features, this feature is built into the markdown core and
-is turned on when `markdown.extensions.extra` or `markdown.extensions.md_in_html`
-is enabled.
+!!! note
-The content of any raw HTML block element can be Markdown-formatted simply by
-adding a `markdown` attribute to the opening tag. The markdown attribute will be
-stripped from the output, but all other attributes will be preserved.
+ The expressions "block-level" and "span-level" as used in this document refer to an element's designation
+ according to the HTML specification. Whereas the `"span"` and `"block"` values assigned to the `markdown`
+ attribute refer to the Markdown parser's behavior.
-If the markdown value is set to `1` (recommended) or any value other than `span`
-or `block`, the default behavior will be executed: `p`,`h[1-6]`,`li`,`dd`,`dt`,
-`td`,`th`,`legend`, and `address` elements skip block parsing while others do not.
-If the default is overridden by a value of `span`, *block parsing will be skipped*
-regardless of tag. If the default is overridden by a value of `block`,
-*block parsing will occur* regardless of tag.
+### `markdown="1"` { #1 }
-#### Simple Example:
+When the `markdown` attribute is set to `"1"`, then the parser will use the default behavior for that specific tag.
-```md
-This is *true* markdown text.
+The following tags have the `block` behavior by default: `address`, `article`, `aside`, `blockquote`, `body`,
+`colgroup`, `details`, `div`, `dl`, `fieldset`, `figcaption`, `figure`, `footer`, `form`, `iframe`, `header`, `hr`,
+`main`, `menu`, `nav`, `map`, `noscript`, `object`, `ol`, `section`, `table`, `tbody`, `thead`, `tfoot`, `tr`, and
+`ul`.
+For example, the following:
+
+```
<div markdown="1">
-This is *true* markdown text.
+This is a *Markdown* Paragraph.
</div>
```
-#### Result:
+... is rendered as:
-```html
-<p>This is <em>true</em> markdown text.</p>
+``` html
<div>
-<p>This is <em>true</em> markdown text.</p>
+<p>This is a <em>Markdown</em> Paragraph.</p>
</div>
```
-### Nested Markdown Inside HTML Blocks
+The following tags have the `span` behavior by default: `address`, `dd`, `dt`, `h[1-6]`, `legend`, `li`, `p`, `td`,
+and `th`.
-Nested elements are more sensitive and must be used cautiously. To avoid
-unexpected results:
+For example, the following:
-* Only nest elements within block mode elements.
-* Follow the closing tag of inner elements with a blank line.
-* Only have one level of nesting.
+```
+<p markdown="1">
+This is not a *Markdown* Paragraph.
+</p>
+```
-#### Complex Example:
+... is rendered as:
-```md
-<div markdown="1" name="Example">
+``` html
+<p>
+This is not a <em>Markdown</em> Paragraph.
+</p>
+```
-The text of the `Example` element.
+### `markdown="block"` { #block }
-<div markdown="1" name="DefaultBlockMode">
-This text gets wrapped in `p` tags.
-</div>
+When the `markdown` attribute is set to `"block"`, then the parser will force the `block` behavior on the contents of
+the element so long as it is one of the `block` or `span` tags.
-The tail of the `DefaultBlockMode` subelement.
+The content of a `block` element is parsed into block-level content. In other words, the text is rendered as
+paragraphs, headers, lists, blockquotes, etc. Any inline syntax within those elements is processed as well.
-<p markdown="1" name="DefaultSpanMode">
-This text *is not* wrapped in additional `p` tags.
-</p>
+For example, the following:
-The tail of the `DefaultSpanMode` subelement.
+```
+<section markdown="block">
+# A header.
-<div markdown="span" name="SpanModeOverride">
-This `div` block is not wrapped in paragraph tags.
-Note: Subelements are not required to have tail text.
-</div>
+A *Markdown* paragraph.
-<p markdown="block" name="BlockModeOverride">
-This `p` block *is* foolishly wrapped in further paragraph tags.
-</p>
+* A list item.
+* A second list item.
-The tail of the `BlockModeOverride` subelement.
+</section>
+```
+
+... is rendered as:
+
+``` html
+<section>
+<h1>A header.</h1>
+<p>A <em>Markdown</em> paragraph.</p>
+<ul>
+<li>A list item.</li>
+<li>A second list item.</li>
+</ul>
+</section>
+```
+
+!!! warning
+
+ Forcing elements to be parsed as `block` elements when they are not by default could result in invalid HTML.
+ For example, one could force a `<p>` element to be nested within another `<p>` element. In most cases it is
+ recommended to use the default behavior of `markdown="1"`. Explicitly setting `markdown="block"` should be
+ reserved for advanced users who understand the HTML specification and how browsers parse and render HTML.
+
+### `markdown="span"` { #span }
+
+When the `markdown` attribute is set to `"span"`, then the parser will force the `span` behavior on the contents
+of the element so long as it is one of the `block` or `span` tags.
+
+The content of a `span` element is not parsed into block-level content. In other words, the content will not be
+rendered as paragraphs, headers, etc. Only inline syntax will be rendered, such as links, strong, emphasis, etc.
+
+For example, the following:
-<div name="RawHtml">
-Raw HTML blocks may also be nested.
+```
+<div markdown="span">
+# *Not* a header
</div>
+```
+... is rendered as:
+
+``` html
+<div>
+# <em>Not</em> a header
</div>
+```
+
+### Ignored Elements
+
+The following tags are always ignored, regardless of any `markdown` attribute: `canvas`, `math`, `option`, `pre`,
+`script`, `style`, and `textarea`. All other raw HTML tags are treated as span-level tags and are not affected by this
+extension.
+
+### Nesting
-This text is after the markdown in HTML.
+When nesting multiple levels of raw HTML elements, a `markdown` attribute must be defined for each block-level
+element. For any block-level element which does not have a `markdown` attribute, everything inside that element is
+ignored, including child elements with `markdown` attributes.
+
+For example, the following:
+
+```
+<article id="my-article" markdown="1">
+# Article Title
+
+A Markdown paragraph.
+
+<section id="section-1" markdown="1">
+## Section 1 Title
+
+<p>Custom raw **HTML** which gets ignored.</p>
+
+</section>
+
+<section id="section-2" markdown="1">
+## Section 2 Title
+
+<p markdown="1">**Markdown** content.</p>
+
+</section>
+
+</article>
```
-#### Complex Result:
+... is rendered as:
```html
-<div name="Example">
-<p>The text of the <code>Example</code> element.</p>
-<div name="DefaultBlockMode">
-<p>This text gets wrapped in <code>p</code> tags.</p>
+<article id="my-article">
+<h1>Article Title</h1>
+<p>A Markdown paragraph.</p>
+<section id="section-1">
+<h2>Section 1 Title</h2>
+<p>Custom raw **HTML** which gets ignored.</p>
+</section>
+<section id="section-2">
+<h2>Section 2 Title</h2>
+<p><strong>Markdown</strong> content.</p>
+</section>
+</article>
+```
+
+When the value of an element's `markdown` attribute is more permissive that its parent, then the parent's stricter
+behavior is enforced. For example, a `block` element nested within a `span` element will be parsed using the `span`
+behavior. However, if the value of an element's `markdown` attribute is the same as, or more restrictive than, its
+parent, the the child element's behavior is observed. For example, a `block` element may contain either `block`
+elements or `span` elements as children and each element will be parsed using the specified behavior.
+
+### Tag Normalization
+
+While the default behavior is for Markdown to not alter raw HTML, as this extension is parsing the content of raw HTML elements, it will do some normalization of the tags of block-level elements. For example, the following raw HTML:
+
+```
+<div markdown="1">
+<p markdown="1">A Markdown paragraph with *no* closing tag.
+<p>A raw paragraph with *no* closing tag.
</div>
-<p>The tail of the <code>DefaultBlockMode</code> subelement.</p>
-<p name="DefaultSpanMode">
-This text <em>is not</em> wrapped in additional <code>p</code> tags.</p>
-<p>The tail of the <code>DefaultSpanMode</code> subelement.</p>
-<div name="SpanModeOverride">
-This <code>div</code> block is not wrapped in paragraph tags.
-Note: Subelements are not required to have tail text.</div>
-<p name="BlockModeOverride">
-<p>This <code>p</code> block <em>is</em> foolishly wrapped in further paragraph tags.</p>
+```
+
+... is rendered as:
+
+``` html
+<div>
+<p>A Markdown paragraph with <em>no</em> closing tag.
+</p>
+<p>A raw paragraph with *no* closing tag.
</p>
-<p>The tail of the <code>BlockModeOverride</code> subelement.</p>
-<div name="RawHtml">
-Raw HTML blocks may also be nested.
</div>
+```
-</div>
-<p>This text is after the markdown in HTML.</p>
+Notice that the parser properly recognizes that an unclosed `<p>` tag ends when another `<p>` tag begins or when the
+parent element ends. In both cases, a closing `</p>` was added to the end of the element, regardless of whether a
+`markdown` attribute was assigned to the element.
+
+To avoid any normalization, an element must not be a descendant of any block-level element which has a `markdown`
+attribute defined.
+
+!!! warning
+
+ The normalization behavior is only documented here so that document authors are not surprised when their carefully
+ crafted raw HTML is altered by Markdown. This extension should not be relied on to normalize and generate valid
+ HTML. For the best results, always include valid raw HTML (with both opening and closing tags) in your Markdown
+ documents.
+
+## Usage
+
+From the Python interpreter:
+
+``` pycon
+>>> import markdown
+>>> html = markdown.markdown(text, extensions=['md_in_html'])
```
</table>
```
+!!! seealso "See Also"
+ The [Attribute Lists](./attr_list.md) extension includes support for defining attributes on table cells.
+
Usage
-----
The callable must return a string appropriate for use in HTML `id` attributes.
+ An alternate version of the default callable supporting Unicode strings is also
+ provided as `markdown.extensions.headerid.slugify_unicode`.
+
* **`separator`**:
Word separator. Character which replaces white space in id. Defaults to "`-`".
# (1, 2, 0, 'beta', 2) => "1.2b2"
# (1, 2, 0, 'rc', 4) => "1.2rc4"
# (1, 2, 0, 'final', 0) => "1.2"
-__version_info__ = (3, 2, 2, 'final', 0)
+__version_info__ = (3, 3, 0, 'final', 0)
def _get_version(version_info):
parser.blockprocessors.register(OListProcessor(parser), 'olist', 40)
parser.blockprocessors.register(UListProcessor(parser), 'ulist', 30)
parser.blockprocessors.register(BlockQuoteProcessor(parser), 'quote', 20)
+ parser.blockprocessors.register(ReferenceProcessor(parser), 'reference', 15)
parser.blockprocessors.register(ParagraphProcessor(parser), 'paragraph', 10)
return parser
RE = re.compile(r'(^|\n)[ ]{0,3}>[ ]?(.*)')
def test(self, parent, block):
- return bool(self.RE.search(block))
+ return bool(self.RE.search(block)) and not util.nearing_recursion_limit()
def run(self, parent, blocks):
block = blocks.pop(0)
class HRProcessor(BlockProcessor):
""" Process Horizontal Rules. """
- RE = r'^[ ]{0,3}((-+[ ]{0,2}){3,}|(_+[ ]{0,2}){3,}|(\*+[ ]{0,2}){3,})[ ]*'
+ RE = r'^[ ]{0,3}((-+[ ]{0,2}){3,}|(_+[ ]{0,2}){3,}|(\*+[ ]{0,2}){3,})[ ]*$'
# Detect hr on any line of a block.
SEARCH_RE = re.compile(RE, re.MULTILINE)
)
+class ReferenceProcessor(BlockProcessor):
+ """ Process link references. """
+ RE = re.compile(
+ r'^[ ]{0,3}\[([^\]]*)\]:[ ]*\n?[ ]*([^\s]+)[ ]*\n?[ ]*((["\'])(.*)\4|\((.*)\))?[ ]*$', re.MULTILINE
+ )
+
+ def test(self, parent, block):
+ return True
+
+ def run(self, parent, blocks):
+ block = blocks.pop(0)
+ m = self.RE.search(block)
+ if m:
+ id = m.group(1).strip().lower()
+ link = m.group(2).lstrip('<').rstrip('>')
+ title = m.group(5) or m.group(6)
+ self.parser.md.references[id] = (link, title)
+ if block[m.end():].strip():
+ # Add any content after match back to blocks as separate block
+ blocks.insert(0, block[m.end():].lstrip('\n'))
+ if block[:m.start()].strip():
+ # Add any content before match back to blocks as separate block
+ blocks.insert(0, block[:m.start()].rstrip('\n'))
+ return True
+ # No match. Restore block.
+ blocks.insert(0, block)
+ return False
+
+
class ParagraphProcessor(BlockProcessor):
""" Process Paragraph blocks. """
'<%s>' % self.doc_tag) + len(self.doc_tag) + 2
end = output.rindex('</%s>' % self.doc_tag)
output = output[start:end].strip()
- except ValueError: # pragma: no cover
+ except ValueError as e: # pragma: no cover
if output.strip().endswith('<%s />' % self.doc_tag):
# We have an empty document
output = ''
else:
# We have a serious problem
raise ValueError('Markdown failed to strip top-level '
- 'tags. Document=%r' % output.strip())
+ 'tags. Document=%r' % output.strip()) from e
# Run the text post-processors
for pp in self.postprocessors:
'''
from . import Extension
-from ..preprocessors import Preprocessor
+from ..blockprocessors import BlockProcessor
from ..inlinepatterns import InlineProcessor
from ..util import AtomicString
import re
import xml.etree.ElementTree as etree
-# Global Vars
-ABBR_REF_RE = re.compile(r'[*]\[(?P<abbr>[^\]]*)\][ ]?:\s*(?P<title>.*)')
-
class AbbrExtension(Extension):
""" Abbreviation Extension for Python-Markdown. """
def extendMarkdown(self, md):
""" Insert AbbrPreprocessor before ReferencePreprocessor. """
- md.preprocessors.register(AbbrPreprocessor(md), 'abbr', 12)
+ md.parser.blockprocessors.register(AbbrPreprocessor(md.parser), 'abbr', 16)
-class AbbrPreprocessor(Preprocessor):
+class AbbrPreprocessor(BlockProcessor):
""" Abbreviation Preprocessor - parse text for abbr references. """
- def run(self, lines):
+ RE = re.compile(r'^[*]\[(?P<abbr>[^\]]*)\][ ]?:[ ]*\n?[ ]*(?P<title>.*)$', re.MULTILINE)
+
+ def test(self, parent, block):
+ return True
+
+ def run(self, parent, blocks):
'''
Find and remove all Abbreviation references from the text.
Each reference is set as a new AbbrPattern in the markdown instance.
'''
- new_text = []
- for line in lines:
- m = ABBR_REF_RE.match(line)
- if m:
- abbr = m.group('abbr').strip()
- title = m.group('title').strip()
- self.md.inlinePatterns.register(
- AbbrInlineProcessor(self._generate_pattern(abbr), title), 'abbr-%s' % abbr, 2
- )
- # Preserve the line to prevent raw HTML indexing issue.
- # https://github.com/Python-Markdown/markdown/issues/584
- new_text.append('')
- else:
- new_text.append(line)
- return new_text
+ block = blocks.pop(0)
+ m = self.RE.search(block)
+ if m:
+ abbr = m.group('abbr').strip()
+ title = m.group('title').strip()
+ self.parser.md.inlinePatterns.register(
+ AbbrInlineProcessor(self._generate_pattern(abbr), title), 'abbr-%s' % abbr, 2
+ )
+ if block[m.end():].strip():
+ # Add any content after match back to blocks as separate block
+ blocks.insert(0, block[m.end():].lstrip('\n'))
+ if block[:m.start()].strip():
+ # Add any content before match back to blocks as separate block
+ blocks.insert(0, block[:m.start()].rstrip('\n'))
+ return True
+ # No match. Restore block.
+ blocks.insert(0, block)
+ return False
def _generate_pattern(self, text):
'''
RE = re.compile(r'(?:^|\n)!!! ?([\w\-]+(?: +[\w\-]+)*)(?: +"(.*?)")? *(?:\n|$)')
RE_SPACES = re.compile(' +')
- def test(self, parent, block):
+ def __init__(self, parser):
+ """Initialization."""
+
+ super().__init__(parser)
+
+ self.current_sibling = None
+ self.content_indention = 0
+
+ def get_sibling(self, parent, block):
+ """Get sibling admontion.
+
+ Retrieve the appropriate siblimg element. This can get trickly when
+ dealing with lists.
+
+ """
+
+ # We already acquired the block via test
+ if self.current_sibling is not None:
+ sibling = self.current_sibling
+ block = block[self.content_indent:]
+ self.current_sibling = None
+ self.content_indent = 0
+ return sibling, block
+
sibling = self.lastChild(parent)
- return self.RE.search(block) or \
- (block.startswith(' ' * self.tab_length) and sibling is not None and
- sibling.get('class', '').find(self.CLASSNAME) != -1)
+
+ if sibling is None or sibling.get('class', '').find(self.CLASSNAME) == -1:
+ sibling = None
+ else:
+ # If the last child is a list and the content is idented sufficient
+ # to be under it, then the content's is sibling is in the list.
+ last_child = self.lastChild(sibling)
+ indent = 0
+ while last_child:
+ if (
+ sibling and block.startswith(' ' * self.tab_length * 2) and
+ last_child and last_child.tag in ('ul', 'ol', 'dl')
+ ):
+
+ # The expectation is that we'll find an <li> or <dt>.
+ # We should get it's last child as well.
+ sibling = self.lastChild(last_child)
+ last_child = self.lastChild(sibling) if sibling else None
+
+ # Context has been lost at this point, so we must adjust the
+ # text's identation level so it will be evaluated correctly
+ # under the list.
+ block = block[self.tab_length:]
+ indent += self.tab_length
+ else:
+ last_child = None
+
+ if not block.startswith(' ' * self.tab_length):
+ sibling = None
+
+ if sibling is not None:
+ self.current_sibling = sibling
+ self.content_indent = indent
+
+ return sibling, block
+
+ def test(self, parent, block):
+
+ if self.RE.search(block):
+ return True
+ else:
+ return self.get_sibling(parent, block)[0] is not None
def run(self, parent, blocks):
- sibling = self.lastChild(parent)
block = blocks.pop(0)
m = self.RE.search(block)
if m:
block = block[m.end():] # removes the first line
+ else:
+ sibling, block = self.get_sibling(parent, block)
block, theRest = self.detab(block)
p.text = title
p.set('class', self.CLASSNAME_TITLE)
else:
+ # Sibling is a list item, but we need to wrap it's content should be wrapped in <p>
+ if sibling.tag in ('li', 'dd') and sibling.text:
+ text = sibling.text
+ sibling.text = ''
+ p = etree.SubElement(sibling, 'p')
+ p.text = text
+
div = sibling
self.parser.parseChunk(div, block)
class AttrListTreeprocessor(Treeprocessor):
- BASE_RE = r'\{\:?([^\}\n]*)\}'
- HEADER_RE = re.compile(r'[ ]+%s[ ]*$' % BASE_RE)
- BLOCK_RE = re.compile(r'\n[ ]*%s[ ]*$' % BASE_RE)
- INLINE_RE = re.compile(r'^%s' % BASE_RE)
+ BASE_RE = r'\{\:?[ ]*([^\}\n ][^\}\n]*)[ ]*\}'
+ HEADER_RE = re.compile(r'[ ]+{}[ ]*$'.format(BASE_RE))
+ BLOCK_RE = re.compile(r'\n[ ]*{}[ ]*$'.format(BASE_RE))
+ INLINE_RE = re.compile(r'^{}'.format(BASE_RE))
NAME_RE = re.compile(r'[^A-Z_a-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff'
r'\u0370-\u037d\u037f-\u1fff\u200c-\u200d'
r'\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff'
if self.md.is_block_level(elem.tag):
# Block level: check for attrs on last line of text
RE = self.BLOCK_RE
- if isheader(elem) or elem.tag == 'dt':
- # header or def-term: check for attrs at end of line
+ if isheader(elem) or elem.tag in ['dt', 'td', 'th']:
+ # header, def-term, or table cell: check for attrs at end of element
RE = self.HEADER_RE
if len(elem) and elem.tag == 'li':
# special case list items. children may include a ul or ol.
elif elem.text:
# no children. Get from text.
m = RE.search(elem.text)
- if not m and elem.tag == 'td':
- m = re.search(self.BASE_RE, elem.text)
if m:
self.assign_attrs(elem, m.group(1))
elem.text = elem.text[:m.start()]
class AttrListExtension(Extension):
def extendMarkdown(self, md):
md.treeprocessors.register(AttrListTreeprocessor(md), 'attr_list', 8)
+ md.registerExtension(self)
def makeExtension(**kwargs): # pragma: no cover
from . import Extension
from ..treeprocessors import Treeprocessor
+from ..util import parseBoolValue
-try:
+try: # pragma: no cover
from pygments import highlight
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.formatters import get_formatter_by_name
pygments = True
-except ImportError:
+except ImportError: # pragma: no cover
pygments = False
try:
return list(map(int, expr.split()))
- except ValueError:
+ except ValueError: # pragma: no cover
return []
# ------------------ The Main CodeHilite Class ----------------------
class CodeHilite:
"""
- Determine language of source code, and pass it into pygments hilighter.
+ Determine language of source code, and pass it on to the Pygments highlighter.
- Basic Usage:
- >>> code = CodeHilite(src = 'some text')
- >>> html = code.hilite()
+ Usage:
+ code = CodeHilite(src=some_code, lang='python')
+ html = code.hilite()
+ Arguments:
* src: Source string or any object with a .readline attribute.
- * linenums: (Boolean) Set line numbering to 'on' (True),
- 'off' (False) or 'auto'(None). Set to 'auto' by default.
+ * lang: String name of Pygments lexer to use for highlighting. Default: `None`.
- * guess_lang: (Boolean) Turn language auto-detection
- 'on' or 'off' (on by default).
+ * guess_lang: Auto-detect which lexer to use. Ignored if `lang` is set to a valid
+ value. Default: `True`.
- * css_class: Set class name of wrapper div ('codehilite' by default).
+ * use_pygments: Pass code to pygments for code highlighting. If `False`, the code is
+ instead wrapped for highlighting by a JavaScript library. Default: `True`.
- * hl_lines: (List of integers) Lines to emphasize, 1-indexed.
+ * linenums: An alias to Pygments `linenos` formatter option. Default: `None`.
- Low Level Usage:
- >>> code = CodeHilite()
- >>> code.src = 'some text' # String or anything with a .readline attr.
- >>> code.linenos = True # Turns line numbering on or of.
- >>> html = code.hilite()
+ * css_class: An alias to Pygments `cssclass` formatter option. Default: 'codehilite'.
+
+ * lang_prefix: Prefix prepended to the language when `use_pygments` is `False`.
+ Default: "language-".
+
+ Other Options:
+ Any other options are accepted and passed on to the lexer and formatter. Therefore,
+ valid options include any options which are accepted by the `html` formatter or
+ whichever lexer the code's language uses. Note that most lexers do not have any
+ options. However, a few have very useful options, such as PHP's `startinline` option.
+ Any invalid options are ignored without error.
+
+ Formatter options: https://pygments.org/docs/formatters/#HtmlFormatter
+ Lexer Options: https://pygments.org/docs/lexers/
+
+ Advanced Usage:
+ code = CodeHilite(
+ src = some_code,
+ lang = 'php',
+ startinline = True, # Lexer option. Snippet does not start with `<?php`.
+ linenostart = 42, # Formatter option. Snippet starts on line 42.
+ hl_lines = [45, 49, 50], # Formatter option. Highlight lines 45, 49, and 50.
+ linenos = 'inline' # Formatter option. Avoid alignment problems.
+ )
+ html = code.hilite()
"""
- def __init__(self, src=None, linenums=None, guess_lang=True,
- css_class="codehilite", lang=None, style='default',
- noclasses=False, tab_length=4, hl_lines=None, use_pygments=True):
+ def __init__(self, src, **options):
self.src = src
- self.lang = lang
- self.linenums = linenums
- self.guess_lang = guess_lang
- self.css_class = css_class
- self.style = style
- self.noclasses = noclasses
- self.tab_length = tab_length
- self.hl_lines = hl_lines or []
- self.use_pygments = use_pygments
+ self.lang = options.pop('lang', None)
+ self.guess_lang = options.pop('guess_lang', True)
+ self.use_pygments = options.pop('use_pygments', True)
+ self.lang_prefix = options.pop('lang_prefix', 'language-')
+
+ if 'linenos' not in options:
+ options['linenos'] = options.pop('linenums', None)
+ if 'cssclass' not in options:
+ options['cssclass'] = options.pop('css_class', 'codehilite')
+ if 'wrapcode' not in options:
+ # Override pygments default
+ options['wrapcode'] = True
+ # Disallow use of `full` option
+ options['full'] = False
+
+ self.options = options
def hilite(self):
"""
if pygments and self.use_pygments:
try:
- lexer = get_lexer_by_name(self.lang)
+ lexer = get_lexer_by_name(self.lang, **self.options)
except ValueError:
try:
if self.guess_lang:
- lexer = guess_lexer(self.src)
+ lexer = guess_lexer(self.src, **self.options)
else:
- lexer = get_lexer_by_name('text')
- except ValueError:
- lexer = get_lexer_by_name('text')
- formatter = get_formatter_by_name('html',
- linenos=self.linenums,
- cssclass=self.css_class,
- style=self.style,
- noclasses=self.noclasses,
- hl_lines=self.hl_lines,
- wrapcode=True)
+ lexer = get_lexer_by_name('text', **self.options)
+ except ValueError: # pragma: no cover
+ lexer = get_lexer_by_name('text', **self.options)
+ formatter = get_formatter_by_name('html', **self.options)
return highlight(self.src, lexer, formatter)
else:
# just escape and build markup usable by JS highlighting libs
txt = txt.replace('"', '"')
classes = []
if self.lang:
- classes.append('language-%s' % self.lang)
- if self.linenums:
+ classes.append('{}{}'.format(self.lang_prefix, self.lang))
+ if self.options['linenos']:
classes.append('linenums')
class_str = ''
if classes:
- class_str = ' class="%s"' % ' '.join(classes)
- return '<pre class="%s"><code%s>%s</code></pre>\n' % \
- (self.css_class, class_str, txt)
+ class_str = ' class="{}"'.format(' '.join(classes))
+ return '<pre class="{}"><code{}>{}\n</code></pre>\n'.format(
+ self.options['cssclass'],
+ class_str,
+ txt
+ )
def _parseHeader(self):
"""
# we have a match
try:
self.lang = m.group('lang').lower()
- except IndexError:
+ except IndexError: # pragma: no cover
self.lang = None
if m.group('path'):
# path exists - restore first line
lines.insert(0, fl)
- if self.linenums is None and m.group('shebang'):
+ if self.options['linenos'] is None and m.group('shebang'):
# Overridable and Shebang exists - use line numbers
- self.linenums = True
+ self.options['linenos'] = True
- self.hl_lines = parse_hl_lines(m.group('hl_lines'))
+ self.options['hl_lines'] = parse_hl_lines(m.group('hl_lines'))
else:
# No match
lines.insert(0, fl)
def code_unescape(self, text):
"""Unescape code."""
- text = text.replace("&", "&")
text = text.replace("<", "<")
text = text.replace(">", ">")
+ # Escaped '&' should be replaced at the end to avoid
+ # conflicting with < and >.
+ text = text.replace("&", "&")
return text
def run(self, root):
if len(block) == 1 and block[0].tag == 'code':
code = CodeHilite(
self.code_unescape(block[0].text),
- linenums=self.config['linenums'],
- guess_lang=self.config['guess_lang'],
- css_class=self.config['css_class'],
- style=self.config['pygments_style'],
- noclasses=self.config['noclasses'],
tab_length=self.md.tab_length,
- use_pygments=self.config['use_pygments']
+ style=self.config.pop('pygments_style', 'default'),
+ **self.config
)
placeholder = self.md.htmlStash.store(code.hilite())
# Clear codeblock in etree instance
# define default configs
self.config = {
'linenums': [None,
- "Use lines numbers. True=yes, False=no, None=auto"],
+ "Use lines numbers. True|table|inline=yes, False=no, None=auto"],
'guess_lang': [True,
"Automatic language detection - Default: True"],
'css_class': ["codehilite",
'use_pygments': [True,
'Use Pygments to Highlight code blocks. '
'Disable if using a JavaScript library. '
- 'Default: True']
+ 'Default: True'],
+ 'lang_prefix': [
+ 'language-',
+ 'Prefix prepended to the language when use_pygments is false. Default: "language-"'
+ ]
}
- super().__init__(**kwargs)
+ for key, value in kwargs.items():
+ if key in self.config:
+ self.setConfig(key, value)
+ else:
+ # manually set unknown keywords.
+ if isinstance(value, str):
+ try:
+ # Attempt to parse str as a bool value
+ value = parseBoolValue(value, preserve_none=True)
+ except ValueError:
+ pass # Assume it's not a bool value. Use as-is.
+ self.config[key] = [value, '']
def extendMarkdown(self, md):
""" Add HilitePostprocessor to Markdown instance. """
raw_block = blocks.pop(0)
m = self.RE.search(raw_block)
- terms = [l.strip() for l in
- raw_block[:m.start()].split('\n') if l.strip()]
+ terms = [term.strip() for term in
+ raw_block[:m.start()].split('\n') if term.strip()]
block = raw_block[m.end():]
no_indent = self.NO_INDENT_RE.match(block)
if no_indent:
class DefListIndentProcessor(ListIndentProcessor):
""" Process indented children of definition list items. """
- ITEM_TYPES = ['dd']
- LIST_TYPES = ['dl']
+ # Defintion lists need to be aware of all list types
+ ITEM_TYPES = ['dd', 'li']
+ LIST_TYPES = ['dl', 'ol', 'ul']
def create_item(self, parent, block):
- """ Create a new dd and parse the block with it as the parent. """
+ """ Create a new dd or li (depending on parent) and parse the block with it as the parent. """
+
dd = etree.SubElement(parent, 'dd')
self.parser.parseBlocks(dd, [block])
License: [BSD](https://opensource.org/licenses/bsd-license.php)
"""
+
+from textwrap import dedent
from . import Extension
from ..preprocessors import Preprocessor
from .codehilite import CodeHilite, CodeHiliteExtension, parse_hl_lines
+from .attr_list import get_attrs, AttrListExtension
+from ..util import parseBoolValue
import re
class FencedCodeExtension(Extension):
+ def __init__(self, **kwargs):
+ self.config = {
+ 'lang_prefix': ['language-', 'Prefix prepended to the language. Default: "language-"']
+ }
+ super().__init__(**kwargs)
def extendMarkdown(self, md):
""" Add FencedBlockPreprocessor to the Markdown instance. """
md.registerExtension(self)
- md.preprocessors.register(FencedBlockPreprocessor(md), 'fenced_code_block', 25)
+ md.preprocessors.register(FencedBlockPreprocessor(md, self.getConfigs()), 'fenced_code_block', 25)
class FencedBlockPreprocessor(Preprocessor):
- FENCED_BLOCK_RE = re.compile(r'''
-(?P<fence>^(?:~{3,}|`{3,}))[ ]* # Opening ``` or ~~~
-(\{?\.?(?P<lang>[\w#.+-]*))?[ ]* # Optional {, and lang
-# Optional highlight lines, single- or double-quote-delimited
-(hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?[ ]*
-}?[ ]*\n # Optional closing }
-(?P<code>.*?)(?<=\n)
-(?P=fence)[ ]*$''', re.MULTILINE | re.DOTALL | re.VERBOSE)
- CODE_WRAP = '<pre><code%s>%s</code></pre>'
- LANG_TAG = ' class="%s"'
-
- def __init__(self, md):
+ FENCED_BLOCK_RE = re.compile(
+ dedent(r'''
+ (?P<fence>^(?:~{3,}|`{3,}))[ ]* # opening fence
+ ((\{(?P<attrs>[^\}\n]*)\})?| # (optional {attrs} or
+ (\.?(?P<lang>[\w#.+-]*))?[ ]* # optional (.)lang
+ (hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?) # optional hl_lines)
+ [ ]*\n # newline (end of opening fence)
+ (?P<code>.*?)(?<=\n) # the code block
+ (?P=fence)[ ]*$ # closing fence
+ '''),
+ re.MULTILINE | re.DOTALL | re.VERBOSE
+ )
+
+ def __init__(self, md, config):
super().__init__(md)
-
- self.checked_for_codehilite = False
+ self.config = config
+ self.checked_for_deps = False
self.codehilite_conf = {}
+ self.use_attr_list = False
+ # List of options to convert to bool values
+ self.bool_options = [
+ 'linenums',
+ 'guess_lang',
+ 'noclasses',
+ 'use_pygments'
+ ]
def run(self, lines):
""" Match and store Fenced Code Blocks in the HtmlStash. """
- # Check for code hilite extension
- if not self.checked_for_codehilite:
+ # Check for dependent extensions
+ if not self.checked_for_deps:
for ext in self.md.registeredExtensions:
if isinstance(ext, CodeHiliteExtension):
- self.codehilite_conf = ext.config
- break
+ self.codehilite_conf = ext.getConfigs()
+ if isinstance(ext, AttrListExtension):
+ self.use_attr_list = True
- self.checked_for_codehilite = True
+ self.checked_for_deps = True
text = "\n".join(lines)
while 1:
m = self.FENCED_BLOCK_RE.search(text)
if m:
- lang = ''
- if m.group('lang'):
- lang = self.LANG_TAG % m.group('lang')
+ lang, id, classes, config = None, '', [], {}
+ if m.group('attrs'):
+ id, classes, config = self.handle_attrs(get_attrs(m.group('attrs')))
+ if len(classes):
+ lang = classes[0]
+ else:
+ if m.group('lang'):
+ lang = m.group('lang')
+ classes.append(lang)
+ if m.group('hl_lines'):
+ # Support hl_lines outside of attrs for backward-compatibility
+ config['hl_lines'] = parse_hl_lines(m.group('hl_lines'))
# If config is not empty, then the codehighlite extension
# is enabled, so we call it to highlight the code
- if self.codehilite_conf:
+ if self.codehilite_conf and self.codehilite_conf['use_pygments'] and config.get('use_pygments', True):
+ local_config = self.codehilite_conf.copy()
+ local_config.update(config)
+ # Combine classes with cssclass. Ensure cssclass is at end
+ # as pygments appends a suffix under certain circumstances.
+ # Ignore ID as Pygments does not offer an option to set it.
+ if classes:
+ local_config['css_class'] = '{} {}'.format(
+ ' '.join(classes),
+ local_config['css_class']
+ )
highliter = CodeHilite(
m.group('code'),
- linenums=self.codehilite_conf['linenums'][0],
- guess_lang=self.codehilite_conf['guess_lang'][0],
- css_class=self.codehilite_conf['css_class'][0],
- style=self.codehilite_conf['pygments_style'][0],
- use_pygments=self.codehilite_conf['use_pygments'][0],
- lang=(m.group('lang') or None),
- noclasses=self.codehilite_conf['noclasses'][0],
- hl_lines=parse_hl_lines(m.group('hl_lines'))
+ lang=lang,
+ style=local_config.pop('pygments_style', 'default'),
+ **local_config
)
code = highliter.hilite()
else:
- code = self.CODE_WRAP % (lang,
- self._escape(m.group('code')))
+ id_attr = class_attr = kv_pairs = ''
+ if classes:
+ class_attr = ' class="{}{}"'.format(
+ self.config.get('lang_prefix', 'language-'),
+ ' '.join(classes)
+ )
+ if id:
+ id_attr = ' id="{}"'.format(id)
+ if self.use_attr_list and config and not config.get('use_pygments', False):
+ # Only assign key/value pairs to code element if attr_list ext is enabled, key/value pairs
+ # were defined on the code block, and the `use_pygments` key was not set to True. The
+ # `use_pygments` key could be either set to False or not defined. It is omitted from output.
+ kv_pairs = ' ' + ' '.join(
+ '{k}="{v}"'.format(k=k, v=v) for k, v in config.items() if k != 'use_pygments'
+ )
+ code = '<pre{id}><code{cls}{kv}>{code}</code></pre>'.format(
+ id=id_attr,
+ cls=class_attr,
+ kv=kv_pairs,
+ code=self._escape(m.group('code'))
+ )
placeholder = self.md.htmlStash.store(code)
text = '{}\n{}\n{}'.format(text[:m.start()],
break
return text.split("\n")
+ def handle_attrs(self, attrs):
+ """ Return tuple: (id, [list, of, classes], {configs}) """
+ id = ''
+ classes = []
+ configs = {}
+ for k, v in attrs:
+ if k == 'id':
+ id = v
+ elif k == '.':
+ classes.append(v)
+ elif k == 'hl_lines':
+ configs[k] = parse_hl_lines(v)
+ elif k in self.bool_options:
+ configs[k] = parseBoolValue(v, fail_on_errors=False, preserve_none=True)
+ else:
+ configs[k] = v
+ return id, classes, configs
+
def _escape(self, txt):
""" basic html escaping """
txt = txt.replace('&', '&')
"""
from . import Extension
-from ..preprocessors import Preprocessor
+from ..blockprocessors import BlockProcessor
from ..inlinepatterns import InlineProcessor
from ..treeprocessors import Treeprocessor
from ..postprocessors import Postprocessor
FN_BACKLINK_TEXT = util.STX + "zz1337820767766393qq" + util.ETX
NBSP_PLACEHOLDER = util.STX + "qq3936677670287331zz" + util.ETX
-DEF_RE = re.compile(r'[ ]{0,3}\[\^([^\]]*)\]:\s*(.*)')
-TABBED_RE = re.compile(r'((\t)|( ))(.*)')
RE_REF_ID = re.compile(r'(fnref)(\d+)')
md.registerExtension(self)
self.parser = md.parser
self.md = md
- # Insert a preprocessor before ReferencePreprocessor
- md.preprocessors.register(FootnotePreprocessor(self), 'footnote', 15)
+ # Insert a blockprocessor before ReferencePreprocessor
+ md.parser.blockprocessors.register(FootnoteBlockProcessor(self), 'footnote', 17)
# Insert an inline pattern before ImageReferencePattern
FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah
return div
-class FootnotePreprocessor(Preprocessor):
+class FootnoteBlockProcessor(BlockProcessor):
""" Find all footnote references and store for later use. """
+ RE = re.compile(r'^[ ]{0,3}\[\^([^\]]*)\]:[ ]*(.*)$', re.MULTILINE)
+
def __init__(self, footnotes):
+ super().__init__(footnotes.parser)
self.footnotes = footnotes
- def run(self, lines):
- """
- Loop through lines and find, set, and remove footnote definitions.
-
- Keywords:
-
- * lines: A list of lines of text
-
- Return: A list of lines of text with footnote definitions removed.
-
- """
- newlines = []
- i = 0
- while True:
- m = DEF_RE.match(lines[i])
- if m:
- fn, _i = self.detectTabbed(lines[i+1:])
- fn.insert(0, m.group(2))
- i += _i-1 # skip past footnote
- footnote = "\n".join(fn)
- self.footnotes.setFootnote(m.group(1), footnote.rstrip())
- # Preserve a line for each block to prevent raw HTML indexing issue.
- # https://github.com/Python-Markdown/markdown/issues/584
- num_blocks = (len(footnote.split('\n\n')) * 2)
- newlines.extend([''] * (num_blocks))
+ def test(self, parent, block):
+ return True
+
+ def run(self, parent, blocks):
+ """ Find, set, and remove footnote definitions. """
+ block = blocks.pop(0)
+ m = self.RE.search(block)
+ if m:
+ id = m.group(1)
+ fn_blocks = [m.group(2)]
+
+ # Handle rest of block
+ therest = block[m.end():].lstrip('\n')
+ m2 = self.RE.search(therest)
+ if m2:
+ # Another footnote exists in the rest of this block.
+ # Any content before match is continuation of this footnote, which may be lazily indented.
+ before = therest[:m2.start()].rstrip('\n')
+ fn_blocks[0] = '\n'.join([fn_blocks[0], self.detab(before)]).lstrip('\n')
+ # Add back to blocks everything from begining of match forward for next iteration.
+ blocks.insert(0, therest[m2.start():])
else:
- newlines.append(lines[i])
- if len(lines) > i+1:
- i += 1
- else:
- break
- return newlines
+ # All remaining lines of block are continuation of this footnote, which may be lazily indented.
+ fn_blocks[0] = '\n'.join([fn_blocks[0], self.detab(therest)]).strip('\n')
- def detectTabbed(self, lines):
- """ Find indented text and remove indent before further proccesing.
+ # Check for child elements in remaining blocks.
+ fn_blocks.extend(self.detectTabbed(blocks))
- Keyword arguments:
+ footnote = "\n\n".join(fn_blocks)
+ self.footnotes.setFootnote(id, footnote.rstrip())
- * lines: an array of strings
+ if block[:m.start()].strip():
+ # Add any content before match back to blocks as separate block
+ blocks.insert(0, block[:m.start()].rstrip('\n'))
+ return True
+ # No match. Restore block.
+ blocks.insert(0, block)
+ return False
- Returns: a list of post processed items and the index of last line.
+ def detectTabbed(self, blocks):
+ """ Find indented text and remove indent before further proccesing.
+ Returns: a list of blocks with indentation removed.
"""
- items = []
- blank_line = False # have we encountered a blank line yet?
- i = 0 # to keep track of where we are
-
- def detab(line):
- match = TABBED_RE.match(line)
- if match:
- return match.group(4)
-
- for line in lines:
- if line.strip(): # Non-blank line
- detabbed_line = detab(line)
- if detabbed_line:
- items.append(detabbed_line)
- i += 1
- continue
- elif not blank_line and not DEF_RE.match(line):
- # not tabbed but still part of first par.
- items.append(line)
- i += 1
- continue
- else:
- return items, i+1
-
- else: # Blank line: _maybe_ we are done.
- blank_line = True
- i += 1 # advance
-
- # Find the next non-blank line
- for j in range(i, len(lines)):
- if lines[j].strip():
- next_line = lines[j]
- break
- else:
- # Include extreaneous padding to prevent raw HTML
- # parsing issue: https://github.com/Python-Markdown/markdown/issues/584
- items.append("")
- i += 1
+ fn_blocks = []
+ while blocks:
+ if blocks[0].startswith(' '*4):
+ block = blocks.pop(0)
+ # Check for new footnotes within this block and split at new footnote.
+ m = self.RE.search(block)
+ if m:
+ # Another footnote exists in this block.
+ # Any content before match is continuation of this footnote, which may be lazily indented.
+ before = block[:m.start()].rstrip('\n')
+ fn_blocks.append(self.detab(before))
+ # Add back to blocks everything from begining of match forward for next iteration.
+ blocks.insert(0, block[m.start():])
+ # End of this footnote.
+ break
else:
- break # There is no more text; we are done.
+ # Entire block is part of this footnote.
+ fn_blocks.append(self.detab(block))
+ else:
+ # End of this footnote.
+ break
+ return fn_blocks
- # Check if the next non-blank line is tabbed
- if detab(next_line): # Yes, more work to do.
- items.append("")
- continue
- else:
- break # No, we are done.
- else:
- i += 1
+ def detab(self, block):
+ """ Remove one level of indent from a block.
- return items, i
+ Preserve lazily indented blocks by only removing indent from indented lines.
+ """
+ lines = block.split('\n')
+ for i, line in enumerate(lines):
+ if line.startswith(' '*4):
+ lines[i] = line[4:]
+ return '\n'.join(lines)
class FootnoteInlineProcessor(InlineProcessor):
self.offset += 1
# Add all the new duplicate links.
el = list(li)[-1]
- for l in links:
- el.append(l)
+ for link in links:
+ el.append(link)
break
def get_num_duplicates(self, li):
STRONG_RE = r'(_{2})(.+?)\1'
# __strong_em___
-STRONG_EM_RE = r'(_)\1(?!\1)(.+?)\1(?!\1)(.+?)\1{3}'
+STRONG_EM_RE = r'(_)\1(?!\1)([^_]+?)\1(?!\1)(.+?)\1{3}'
class LegacyUnderscoreProcessor(UnderscoreProcessor):
from . import Extension
from ..blockprocessors import BlockProcessor
+from ..preprocessors import Preprocessor
from .. import util
-import re
+from ..htmlparser import HTMLExtractor
import xml.etree.ElementTree as etree
+# Block-level tags in which the content only gets span level parsing
+span_tags = ['address', 'dd', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'legend', 'li', 'p', 'td', 'th']
+
+# Block-level tags in which the content gets parsed as blocks
+block_tags = [
+ 'address', 'article', 'aside', 'blockquote', 'body', 'colgroup', 'details', 'div', 'dl', 'fieldset',
+ 'figcaption', 'figure', 'footer', 'form', 'iframe', 'header', 'hr', 'main', 'menu', 'nav', 'map',
+ 'noscript', 'object', 'ol', 'section', 'table', 'tbody', 'thead', 'tfoot', 'tr', 'ul'
+]
+
+# Block-level tags which never get their content parsed.
+raw_tags = ['canvas', 'math', 'option', 'pre', 'script', 'style', 'textarea']
+
+block_level_tags = span_tags + block_tags + raw_tags
+
+
+class HTMLExtractorExtra(HTMLExtractor):
+ """
+ Override HTMLExtractor and create etree Elements for any elements which should have content parsed as Markdown.
+ """
+
+ def reset(self):
+ """Reset this instance. Loses all unprocessed data."""
+ self.mdstack = [] # When markdown=1, stack contains a list of tags
+ self.treebuilder = etree.TreeBuilder()
+ self.mdstate = [] # one of 'block', 'span', 'off', or None
+ super().reset()
+
+ def close(self):
+ """Handle any buffered data."""
+ super().close()
+ # Handle any unclosed tags.
+ if self.mdstack:
+ # Close the outermost parent. handle_endtag will close all unclosed children.
+ self.handle_endtag(self.mdstack[0])
+
+ def get_element(self):
+ """ Return element from treebuilder and reset treebuilder for later use. """
+ element = self.treebuilder.close()
+ self.treebuilder = etree.TreeBuilder()
+ return element
+
+ def get_state(self, tag, attrs):
+ """ Return state from tag and `markdown` attr. One of 'block', 'span', or 'off'. """
+ md_attr = attrs.get('markdown', '0')
+ if md_attr == 'markdown':
+ # `<tag markdown>` is the same as `<tag markdown='1'>`.
+ md_attr = '1'
+ parent_state = self.mdstate[-1] if self.mdstate else None
+ if parent_state == 'off' or (parent_state == 'span' and md_attr != '0'):
+ # Only use the parent state if it is more restrictive than the markdown attribute.
+ md_attr = parent_state
+ if ((md_attr == '1' and tag in block_tags) or
+ (md_attr == 'block' and tag in span_tags + block_tags)):
+ return 'block'
+ elif ((md_attr == '1' and tag in span_tags) or
+ (md_attr == 'span' and tag in span_tags + block_tags)):
+ return 'span'
+ elif tag in block_level_tags:
+ return 'off'
+ else: # pragma: no cover
+ return None
+
+ def handle_starttag(self, tag, attrs):
+ if tag in block_level_tags:
+ # Valueless attr (ex: `<tag checked>`) results in `[('checked', None)]`.
+ # Convert to `{'checked': 'checked'}`.
+ attrs = {key: value if value is not None else key for key, value in attrs}
+ state = self.get_state(tag, attrs)
+
+ if self.inraw or (state in [None, 'off'] and not self.mdstack):
+ # fall back to default behavior
+ attrs.pop('markdown', None)
+ super().handle_starttag(tag, attrs)
+ else:
+ if 'p' in self.mdstack and tag in block_level_tags:
+ # Close unclosed 'p' tag
+ self.handle_endtag('p')
+ self.mdstate.append(state)
+ self.mdstack.append(tag)
+ attrs['markdown'] = state
+ self.treebuilder.start(tag, attrs)
+ else:
+ # Span level tag
+ if self.inraw:
+ super().handle_starttag(tag, attrs)
+ else:
+ text = self.get_starttag_text()
+ self.handle_data(text)
+
+ def handle_endtag(self, tag):
+ if tag in block_level_tags:
+ if self.inraw:
+ super().handle_endtag(tag)
+ elif tag in self.mdstack:
+ # Close element and any unclosed children
+ while self.mdstack:
+ item = self.mdstack.pop()
+ self.mdstate.pop()
+ self.treebuilder.end(item)
+ if item == tag:
+ break
+ if not self.mdstack:
+ # Last item in stack is closed. Stash it
+ element = self.get_element()
+ self.cleandoc.append(self.md.htmlStash.store(element))
+ self.cleandoc.append('\n\n')
+ self.state = []
+ else:
+ # Treat orphan closing tag as a span level tag.
+ text = self.get_endtag_text(tag)
+ self.handle_data(text)
+ else:
+ # Span level tag
+ if self.inraw:
+ super().handle_endtag(tag)
+ else:
+ text = self.get_endtag_text(tag)
+ self.handle_data(text)
+
+ def handle_data(self, data):
+ if self.inraw or not self.mdstack:
+ super().handle_data(data)
+ else:
+ self.treebuilder.data(data)
+
+ def handle_empty_tag(self, data, is_block):
+ if self.inraw or not self.mdstack:
+ super().handle_empty_tag(data, is_block)
+ else:
+ if self.at_line_start() and is_block:
+ self.handle_data('\n' + self.md.htmlStash.store(data) + '\n\n')
+ else:
+ self.handle_data(data)
+
+
+class HtmlBlockPreprocessor(Preprocessor):
+ """Remove html blocks from the text and store them for later retrieval."""
+
+ def run(self, lines):
+ source = '\n'.join(lines)
+ parser = HTMLExtractorExtra(self.md)
+ parser.feed(source)
+ parser.close()
+ return ''.join(parser.cleandoc).split('\n')
+
+
class MarkdownInHtmlProcessor(BlockProcessor):
- """Process Markdown Inside HTML Blocks."""
+ """Process Markdown Inside HTML Blocks which have been stored in the HtmlStash."""
+
def test(self, parent, block):
- return block == util.TAG_PLACEHOLDER % \
- str(self.parser.blockprocessors.tag_counter + 1)
-
- def _process_nests(self, element, block):
- """Process the element's child elements in self.run."""
- # Build list of indexes of each nest within the parent element.
- nest_index = [] # a list of tuples: (left index, right index)
- i = self.parser.blockprocessors.tag_counter + 1
- while len(self._tag_data) > i and self._tag_data[i]['left_index']:
- left_child_index = self._tag_data[i]['left_index']
- right_child_index = self._tag_data[i]['right_index']
- nest_index.append((left_child_index - 1, right_child_index))
- i += 1
-
- # Create each nest subelement.
- for i, (left_index, right_index) in enumerate(nest_index[:-1]):
- self.run(element, block[left_index:right_index],
- block[right_index:nest_index[i + 1][0]], True)
- self.run(element, block[nest_index[-1][0]:nest_index[-1][1]], # last
- block[nest_index[-1][1]:], True) # nest
-
- def run(self, parent, blocks, tail=None, nest=False):
- self._tag_data = self.parser.md.htmlStash.tag_data
-
- self.parser.blockprocessors.tag_counter += 1
- tag = self._tag_data[self.parser.blockprocessors.tag_counter]
-
- # Create Element
- markdown_value = tag['attrs'].pop('markdown')
- element = etree.SubElement(parent, tag['tag'], tag['attrs'])
-
- # Slice Off Block
- if nest:
- self.parser.parseBlocks(parent, tail) # Process Tail
- block = blocks[1:]
- else: # includes nests since a third level of nesting isn't supported
- block = blocks[tag['left_index'] + 1: tag['right_index']]
- del blocks[:tag['right_index']]
-
- # Process Text
- if (self.parser.blockprocessors.contain_span_tags.match( # Span Mode
- tag['tag']) and markdown_value != 'block') or \
- markdown_value == 'span':
- element.text = '\n'.join(block)
- else: # Block Mode
- i = self.parser.blockprocessors.tag_counter + 1
- if len(self._tag_data) > i and self._tag_data[i]['left_index']:
- first_subelement_index = self._tag_data[i]['left_index'] - 1
- self.parser.parseBlocks(
- element, block[:first_subelement_index])
- if not nest:
- block = self._process_nests(element, block)
- else:
- self.parser.parseBlocks(element, block)
+ # ALways return True. `run` will return `False` it not a valid match.
+ return True
+
+ def parse_element_content(self, element):
+ """
+ Resursively parse the text content of an etree Element as Markdown.
+
+ Any block level elements generated from the Markdown will be inserted as children of the element in place
+ of the text content. All `markdown` attributes are removed. For any elements in which Markdown parsing has
+ been dissabled, the text content of it and its chidlren are wrapped in an `AtomicString`.
+ """
+
+ md_attr = element.attrib.pop('markdown', 'off')
+
+ if md_attr == 'block':
+ # Parse content as block level
+ # The order in which the different parts are parsed (text, children, tails) is important here as the
+ # order of elements needs to be preserved. We can't be inserting items at a later point in the current
+ # iteration as we don't want to do raw processing on elements created from parsing Markdown text (for
+ # example). Therefore, the order of operations is children, tails, text.
+
+ # Recursively parse existing children from raw HTML
+ for child in list(element):
+ self.parse_element_content(child)
+
+ # Parse Markdown text in tail of children. Do this seperate to avoid raw HTML parsing.
+ # Save the position of each item to be inserted later in reverse.
+ tails = []
+ for pos, child in enumerate(element):
+ if child.tail:
+ block = child.tail.rstrip('\n')
+ child.tail = ''
+ # Use a dummy placeholder element.
+ dummy = etree.Element('div')
+ self.parser.parseBlocks(dummy, block.split('\n\n'))
+ children = list(dummy)
+ children.reverse()
+ tails.append((pos + 1, children))
+
+ # Insert the elements created from the tails in reverse.
+ tails.reverse()
+ for pos, tail in tails:
+ for item in tail:
+ element.insert(pos, item)
+
+ # Parse Markdown text content. Do this last to avoid raw HTML parsing.
+ if element.text:
+ block = element.text.rstrip('\n')
+ element.text = ''
+ # Use a dummy placeholder element as the content needs to get inserted before existing children.
+ dummy = etree.Element('div')
+ self.parser.parseBlocks(dummy, block.split('\n\n'))
+ children = list(dummy)
+ children.reverse()
+ for child in children:
+ element.insert(0, child)
+
+ elif md_attr == 'span':
+ # Span level parsing will be handled by inlineprocessors.
+ # Walk children here to remove any `markdown` attributes.
+ for child in list(element):
+ self.parse_element_content(child)
+
+ else:
+ # Disable inline parsing for everything else
+ element.text = util.AtomicString(element.text)
+ for child in list(element):
+ self.parse_element_content(child)
+ if child.tail:
+ child.tail = util.AtomicString(child.tail)
+
+ def run(self, parent, blocks):
+ m = util.HTML_PLACEHOLDER_RE.match(blocks[0])
+ if m:
+ index = int(m.group(1))
+ element = self.parser.md.htmlStash.rawHtmlBlocks[index]
+ if isinstance(element, etree.Element):
+ # We have a matched element. Process it.
+ blocks.pop(0)
+ self.parse_element_content(element)
+ parent.append(element)
+ # Cleanup stash. Replace element with empty string to avoid confusing postprocessor.
+ self.parser.md.htmlStash.rawHtmlBlocks.pop(index)
+ self.parser.md.htmlStash.rawHtmlBlocks.insert(index, '')
+ # Comfirm the match to the blockparser.
+ return True
+ # No match found.
+ return False
class MarkdownInHtmlExtension(Extension):
def extendMarkdown(self, md):
""" Register extension instances. """
- # Turn on processing of markdown text within raw html
- md.preprocessors['html_block'].markdown_in_raw = True
+ # Replace raw HTML preprocessor
+ md.preprocessors.register(HtmlBlockPreprocessor(md), 'html_block', 20)
+ # Add blockprocessor which handles the placeholders for etree elements
md.parser.blockprocessors.register(
MarkdownInHtmlProcessor(md.parser), 'markdown_block', 105
)
- md.parser.blockprocessors.tag_counter = -1
- md.parser.blockprocessors.contain_span_tags = re.compile(
- r'^(p|h[1-6]|li|dd|dt|td|th|legend|address)$', re.IGNORECASE)
def makeExtension(**kwargs): # pragma: no cover
import xml.etree.ElementTree as etree
-def slugify(value, separator):
+def slugify(value, separator, encoding='ascii'):
""" Slugify a string, to make it URL friendly. """
- value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
- value = re.sub(r'[^\w\s-]', '', value.decode('ascii')).strip().lower()
- return re.sub(r'[%s\s]+' % separator, separator, value)
+ value = unicodedata.normalize('NFKD', value).encode(encoding, 'ignore')
+ value = re.sub(r'[^\w\s-]', '', value.decode(encoding)).strip().lower()
+ return re.sub(r'[{}\s]+'.format(separator), separator, value)
+
+
+def slugify_unicode(value, separator):
+ """ Slugify a string, to make it URL friendly while preserving Unicode characters. """
+ return slugify(value, separator, 'utf-8')
IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$')
--- /dev/null
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2020 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+import re
+import importlib
+import sys
+
+
+# Import a copy of the html.parser lib as `htmlparser` so we can monkeypatch it.
+# Users can still do `from html import parser` and get the default behavior.
+spec = importlib.util.find_spec('html.parser')
+htmlparser = importlib.util.module_from_spec(spec)
+spec.loader.exec_module(htmlparser)
+sys.modules['htmlparser'] = htmlparser
+
+# Monkeypatch HTMLParser to only accept `?>` to close Processing Instructions.
+htmlparser.piclose = re.compile(r'\?>')
+# Monkeypatch HTMLParser to only recognize entity references with a closing semicolon.
+htmlparser.entityref = re.compile(r'&([a-zA-Z][-.a-zA-Z0-9]*);')
+# Monkeypatch HTMLParser to no longer support partial entities. We are always feeding a complete block,
+# so the 'incomplete' functionality is unnecessary. As the entityref regex is run right before incomplete,
+# and the two regex are the same, then incomplete will simply never match and we avoid the logic within.
+htmlparser.incomplete = htmlparser.entityref
+
+# Match a blank line at the start of a block of text (two newlines).
+# The newlines may be preceded by additional whitespace.
+blank_line_re = re.compile(r'^([ ]*\n){2}')
+
+
+class HTMLExtractor(htmlparser.HTMLParser):
+ """
+ Extract raw HTML from text.
+
+ The raw HTML is stored in the `htmlStash` of the Markdown instance passed
+ to `md` and the remaining text is stored in `cleandoc` as a list of strings.
+ """
+
+ def __init__(self, md, *args, **kwargs):
+ if 'convert_charrefs' not in kwargs:
+ kwargs['convert_charrefs'] = False
+ # This calls self.reset
+ super().__init__(*args, **kwargs)
+ self.md = md
+
+ def reset(self):
+ """Reset this instance. Loses all unprocessed data."""
+ self.inraw = False
+ self.intail = False
+ self.stack = [] # When inraw==True, stack contains a list of tags
+ self._cache = []
+ self.cleandoc = []
+ super().reset()
+
+ def close(self):
+ """Handle any buffered data."""
+ super().close()
+ # Handle any unclosed tags.
+ if len(self._cache):
+ self.cleandoc.append(self.md.htmlStash.store(''.join(self._cache)))
+ self._cache = []
+
+ @property
+ def line_offset(self):
+ """Returns char index in self.rawdata for the start of the current line. """
+ if self.lineno > 1:
+ return re.match(r'([^\n]*\n){{{}}}'.format(self.lineno-1), self.rawdata).end()
+ return 0
+
+ def at_line_start(self):
+ """
+ Returns True if current position is at start of line.
+
+ Allows for up to three blank spaces at start of line.
+ """
+ if self.offset == 0:
+ return True
+ if self.offset > 3:
+ return False
+ # Confirm up to first 3 chars are whitespace
+ return self.rawdata[self.line_offset:self.line_offset + self.offset].strip() == ''
+
+ def get_endtag_text(self, tag):
+ """
+ Returns the text of the end tag.
+
+ If it fails to extract the actual text from the raw data, it builds a closing tag with `tag`.
+ """
+ # Attempt to extract actual tag from raw source text
+ start = self.line_offset + self.offset
+ m = htmlparser.endendtag.search(self.rawdata, start)
+ if m:
+ return self.rawdata[start:m.end()]
+ else: # pragma: no cover
+ # Failed to extract from raw data. Assume well formed and lowercase.
+ return '</{}>'.format(tag)
+
+ def handle_starttag(self, tag, attrs):
+ if self.md.is_block_level(tag) and (self.intail or (self.at_line_start() and not self.inraw)):
+ # Started a new raw block. Prepare stack.
+ self.inraw = True
+ self.cleandoc.append('\n')
+
+ text = self.get_starttag_text()
+ if self.inraw:
+ self.stack.append(tag)
+ self._cache.append(text)
+ else:
+ self.cleandoc.append(text)
+
+ def handle_endtag(self, tag):
+ text = self.get_endtag_text(tag)
+
+ if self.inraw:
+ self._cache.append(text)
+ if tag in self.stack:
+ # Remove tag from stack
+ while self.stack:
+ if self.stack.pop() == tag:
+ break
+ if len(self.stack) == 0:
+ # End of raw block.
+ if blank_line_re.match(self.rawdata[self.line_offset + self.offset + len(text):]):
+ # Preserve blank line and end of raw block.
+ self._cache.append('\n')
+ else:
+ # More content exists after endtag.
+ self.intail = True
+ # Reset stack.
+ self.inraw = False
+ self.cleandoc.append(self.md.htmlStash.store(''.join(self._cache)))
+ # Insert blank line between this and next line.
+ self.cleandoc.append('\n\n')
+ self._cache = []
+ else:
+ self.cleandoc.append(text)
+
+ def handle_data(self, data):
+ if self.intail and '\n' in data:
+ self.intail = False
+ if self.inraw:
+ self._cache.append(data)
+ else:
+ self.cleandoc.append(data)
+
+ def handle_empty_tag(self, data, is_block):
+ """ Handle empty tags (`<data>`). """
+ if self.inraw or self.intail:
+ # Append this to the existing raw block
+ self._cache.append(data)
+ elif self.at_line_start() and is_block:
+ # Handle this as a standalone raw block
+ if blank_line_re.match(self.rawdata[self.line_offset + self.offset + len(data):]):
+ # Preserve blank line after tag in raw block.
+ data += '\n'
+ else:
+ # More content exists after tag.
+ self.intail = True
+ self.cleandoc.append(self.md.htmlStash.store(data))
+ # Insert blank line between this and next line.
+ self.cleandoc.append('\n\n')
+ else:
+ self.cleandoc.append(data)
+
+ def handle_startendtag(self, tag, attrs):
+ self.handle_empty_tag(self.get_starttag_text(), is_block=self.md.is_block_level(tag))
+
+ def handle_charref(self, name):
+ self.handle_empty_tag('&#{};'.format(name), is_block=False)
+
+ def handle_entityref(self, name):
+ self.handle_empty_tag('&{};'.format(name), is_block=False)
+
+ def handle_comment(self, data):
+ self.handle_empty_tag('<!--{}-->'.format(data), is_block=True)
+
+ def handle_decl(self, data):
+ self.handle_empty_tag('<!{}>'.format(data), is_block=True)
+
+ def handle_pi(self, data):
+ self.handle_empty_tag('<?{}?>'.format(data), is_block=True)
+
+ def unknown_decl(self, data):
+ end = ']]>' if data.startswith('CDATA[') else ']>'
+ self.handle_empty_tag('<![{}{}'.format(data, end), is_block=True)
inlinePatterns.register(
ShortReferenceInlineProcessor(REFERENCE_RE, md), 'short_reference', 130
)
+ inlinePatterns.register(
+ ShortImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md), 'short_image_ref', 125
+ )
inlinePatterns.register(AutolinkInlineProcessor(AUTOLINK_RE, md), 'autolink', 120)
inlinePatterns.register(AutomailInlineProcessor(AUTOMAIL_RE, md), 'automail', 110)
inlinePatterns.register(SubstituteTagInlineProcessor(LINE_BREAK_RE, 'br'), 'linebreak', 100)
# ___strong__em_
STRONG_EM2_RE = r'(_)\1{2}(.+?)\1{2}(.*?)\1'
-# __strong_em___
-STRONG_EM3_RE = r'(\*)\1(?!\1)(.+?)\1(?!\1)(.+?)\1{3}'
+# **strong*em***
+STRONG_EM3_RE = r'(\*)\1(?!\1)([^*]+?)\1(?!\1)(.+?)\1{3}'
# [text](url) or [text](<url>) or [text](url "title")
LINK_RE = NOIMG + r'\['
return el
+class ShortImageReferenceInlineProcessor(ImageReferenceInlineProcessor):
+ """ Short form of inage reference: ![ref]. """
+ def evalId(self, data, index, text):
+ """Evaluate the id from of [ref] """
+
+ return text.lower(), index, True
+
+
class AutolinkInlineProcessor(InlineProcessor):
""" Return a link Element given an autolink (`<http://example/com>`). """
def handleMatch(self, m, data):
for i in range(self.md.htmlStash.html_counter):
html = self.md.htmlStash.rawHtmlBlocks[i]
if self.isblocklevel(html):
- replacements["<p>%s</p>" %
- (self.md.htmlStash.get_placeholder(i))] = \
- html + "\n"
+ replacements["<p>{}</p>".format(
+ self.md.htmlStash.get_placeholder(i))] = html
replacements[self.md.htmlStash.get_placeholder(i)] = html
if replacements:
"""
from . import util
+from .htmlparser import HTMLExtractor
import re
preprocessors = util.Registry()
preprocessors.register(NormalizeWhitespace(md), 'normalize_whitespace', 30)
preprocessors.register(HtmlBlockPreprocessor(md), 'html_block', 20)
- preprocessors.register(ReferencePreprocessor(md), 'reference', 10)
return preprocessors
class HtmlBlockPreprocessor(Preprocessor):
"""Remove html blocks from the text and store them for later retrieval."""
- right_tag_patterns = ["</%s>", "%s>"]
- attrs_pattern = r"""
- \s+(?P<attr>[^>"'/= ]+)=(?P<q>['"])(?P<value>.*?)(?P=q) # attr="value"
- | # OR
- \s+(?P<attr1>[^>"'/= ]+)=(?P<value1>[^> ]+) # attr=value
- | # OR
- \s+(?P<attr2>[^>"'/= ]+) # attr
- """
- left_tag_pattern = r'^\<(?P<tag>[^> ]+)(?P<attrs>(%s)*)\s*\/?\>?' % \
- attrs_pattern
- attrs_re = re.compile(attrs_pattern, re.VERBOSE)
- left_tag_re = re.compile(left_tag_pattern, re.VERBOSE)
- markdown_in_raw = False
-
- def _get_left_tag(self, block):
- m = self.left_tag_re.match(block)
- if m:
- tag = m.group('tag')
- raw_attrs = m.group('attrs')
- attrs = {}
- if raw_attrs:
- for ma in self.attrs_re.finditer(raw_attrs):
- if ma.group('attr'):
- if ma.group('value'):
- attrs[ma.group('attr').strip()] = ma.group('value')
- else:
- attrs[ma.group('attr').strip()] = ""
- elif ma.group('attr1'):
- if ma.group('value1'):
- attrs[ma.group('attr1').strip()] = ma.group(
- 'value1'
- )
- else:
- attrs[ma.group('attr1').strip()] = ""
- elif ma.group('attr2'):
- attrs[ma.group('attr2').strip()] = ""
- return tag, len(m.group(0)), attrs
- else:
- tag = block[1:].split(">", 1)[0].lower()
- return tag, len(tag)+2, {}
-
- def _recursive_tagfind(self, ltag, rtag, start_index, block):
- while 1:
- i = block.find(rtag, start_index)
- if i == -1:
- return -1
- j = block.find(ltag, start_index)
- # if no ltag, or rtag found before another ltag, return index
- if (j > i or j == -1):
- return i + len(rtag)
- # another ltag found before rtag, use end of ltag as starting
- # point and search again
- j = block.find('>', j)
- start_index = self._recursive_tagfind(ltag, rtag, j + 1, block)
- if start_index == -1:
- # HTML potentially malformed- ltag has no corresponding
- # rtag
- return -1
-
- def _get_right_tag(self, left_tag, left_index, block):
- for p in self.right_tag_patterns:
- tag = p % left_tag
- i = self._recursive_tagfind(
- "<%s" % left_tag, tag, left_index, block
- )
- if i > 2:
- return tag.lstrip("<").rstrip(">"), i
- return block.rstrip()[-left_index:-1].lower(), len(block)
-
- def _equal_tags(self, left_tag, right_tag):
- if left_tag[0] in ['?', '@', '%']: # handle PHP, etc.
- return True
- if ("/" + left_tag) == right_tag:
- return True
- if (right_tag == "--" and left_tag == "--"):
- return True
- elif left_tag == right_tag[1:] and right_tag[0] == "/":
- return True
- else:
- return False
-
- def _is_oneliner(self, tag):
- return (tag in ['hr', 'hr/'])
-
- def _stringindex_to_listindex(self, stringindex, items):
- """
- Same effect as concatenating the strings in items,
- finding the character to which stringindex refers in that string,
- and returning the index of the item in which that character resides.
- """
- items.append('dummy')
- i, count = 0, 0
- while count <= stringindex:
- count += len(items[i])
- i += 1
- return i - 1
-
- def _nested_markdown_in_html(self, items):
- """Find and process html child elements of the given element block."""
- for i, item in enumerate(items):
- if self.left_tag_re.match(item):
- left_tag, left_index, attrs = \
- self._get_left_tag(''.join(items[i:]))
- right_tag, data_index = self._get_right_tag(
- left_tag, left_index, ''.join(items[i:]))
- right_listindex = \
- self._stringindex_to_listindex(data_index, items[i:]) + i
- if 'markdown' in attrs.keys():
- items[i] = items[i][left_index:] # remove opening tag
- placeholder = self.md.htmlStash.store_tag(
- left_tag, attrs, i + 1, right_listindex + 1)
- items.insert(i, placeholder)
- if len(items) - right_listindex <= 1: # last nest, no tail
- right_listindex -= 1
- items[right_listindex] = items[right_listindex][
- :-len(right_tag) - 2] # remove closing tag
- else: # raw html
- if len(items) - right_listindex <= 1: # last element
- right_listindex -= 1
- if right_listindex <= i:
- right_listindex = i + 1
- placeholder = self.md.htmlStash.store('\n\n'.join(
- items[i:right_listindex]))
- del items[i:right_listindex]
- items.insert(i, placeholder)
- return items
-
- def run(self, lines):
- text = "\n".join(lines)
- new_blocks = []
- text = text.rsplit("\n\n")
- items = []
- left_tag = ''
- right_tag = ''
- in_tag = False # flag
-
- while text:
- block = text[0]
- if block.startswith("\n"):
- block = block[1:]
- text = text[1:]
-
- if block.startswith("\n"):
- block = block[1:]
-
- if not in_tag:
- if block.startswith("<") and len(block.strip()) > 1:
-
- if block[1:4] == "!--":
- # is a comment block
- left_tag, left_index, attrs = "--", 2, {}
- else:
- left_tag, left_index, attrs = self._get_left_tag(block)
- right_tag, data_index = self._get_right_tag(left_tag,
- left_index,
- block)
- # keep checking conditions below and maybe just append
-
- if data_index < len(block) and (self.md.is_block_level(left_tag) or left_tag == '--'):
- text.insert(0, block[data_index:])
- block = block[:data_index]
-
- if not (self.md.is_block_level(left_tag) or block[1] in ["!", "?", "@", "%"]):
- new_blocks.append(block)
- continue
-
- if self._is_oneliner(left_tag):
- new_blocks.append(block.strip())
- continue
-
- if block.rstrip().endswith(">") \
- and self._equal_tags(left_tag, right_tag):
- if self.markdown_in_raw and 'markdown' in attrs.keys():
- block = block[left_index:-len(right_tag) - 2]
- new_blocks.append(self.md.htmlStash.
- store_tag(left_tag, attrs, 0, 2))
- new_blocks.extend([block])
- else:
- new_blocks.append(
- self.md.htmlStash.store(block.strip()))
- continue
- else:
- # if is block level tag and is not complete
- if (not self._equal_tags(left_tag, right_tag)) and \
- (self.md.is_block_level(left_tag) or left_tag == "--"):
- items.append(block.strip())
- in_tag = True
- else:
- new_blocks.append(
- self.md.htmlStash.store(block.strip())
- )
- continue
-
- else:
- new_blocks.append(block)
-
- else:
- items.append(block)
-
- # Need to evaluate all items so we can calculate relative to the left index.
- right_tag, data_index = self._get_right_tag(left_tag, left_index, ''.join(items))
- # Adjust data_index: relative to items -> relative to last block
- prev_block_length = 0
- for item in items[:-1]:
- prev_block_length += len(item)
- data_index -= prev_block_length
-
- if self._equal_tags(left_tag, right_tag):
- # if find closing tag
-
- if data_index < len(block):
- # we have more text after right_tag
- items[-1] = block[:data_index]
- text.insert(0, block[data_index:])
-
- in_tag = False
- if self.markdown_in_raw and 'markdown' in attrs.keys():
- items[0] = items[0][left_index:]
- items[-1] = items[-1][:-len(right_tag) - 2]
- if items[len(items) - 1]: # not a newline/empty string
- right_index = len(items) + 3
- else:
- right_index = len(items) + 2
- new_blocks.append(self.md.htmlStash.store_tag(
- left_tag, attrs, 0, right_index))
- placeholderslen = len(self.md.htmlStash.tag_data)
- new_blocks.extend(
- self._nested_markdown_in_html(items))
- nests = len(self.md.htmlStash.tag_data) - \
- placeholderslen
- self.md.htmlStash.tag_data[-1 - nests][
- 'right_index'] += nests - 2
- else:
- new_blocks.append(
- self.md.htmlStash.store('\n\n'.join(items)))
- items = []
-
- if items:
- if self.markdown_in_raw and 'markdown' in attrs.keys():
- items[0] = items[0][left_index:]
- items[-1] = items[-1][:-len(right_tag) - 2]
- if items[len(items) - 1]: # not a newline/empty string
- right_index = len(items) + 3
- else:
- right_index = len(items) + 2
- new_blocks.append(
- self.md.htmlStash.store_tag(
- left_tag, attrs, 0, right_index))
- placeholderslen = len(self.md.htmlStash.tag_data)
- new_blocks.extend(self._nested_markdown_in_html(items))
- nests = len(self.md.htmlStash.tag_data) - placeholderslen
- self.md.htmlStash.tag_data[-1 - nests][
- 'right_index'] += nests - 2
- else:
- new_blocks.append(
- self.md.htmlStash.store('\n\n'.join(items)))
- new_blocks.append('\n')
-
- new_text = "\n\n".join(new_blocks)
- return new_text.split("\n")
-
-
-class ReferencePreprocessor(Preprocessor):
- """ Remove reference definitions from text and store for later use. """
-
- TITLE = r'[ ]*(\"(.*)\"|\'(.*)\'|\((.*)\))[ ]*'
- RE = re.compile(
- r'^[ ]{0,3}\[([^\]]*)\]:\s*([^ ]*)[ ]*(%s)?$' % TITLE, re.DOTALL
- )
- TITLE_RE = re.compile(r'^%s$' % TITLE)
-
def run(self, lines):
- new_text = []
- while lines:
- line = lines.pop(0)
- m = self.RE.match(line)
- if m:
- id = m.group(1).strip().lower()
- link = m.group(2).lstrip('<').rstrip('>')
- t = m.group(5) or m.group(6) or m.group(7)
- if not t:
- # Check next line for title
- tm = self.TITLE_RE.match(lines[0])
- if tm:
- lines.pop(0)
- t = tm.group(2) or tm.group(3) or tm.group(4)
- self.md.references[id] = (link, t)
- # Preserve the line to prevent raw HTML indexing issue.
- # https://github.com/Python-Markdown/markdown/issues/584
- new_text.append('')
- else:
- new_text.append(line)
-
- return new_text # + "\n"
+ source = '\n'.join(lines)
+ parser = HTMLExtractor(self.md)
+ parser.feed(source)
+ parser.close()
+ return ''.join(parser.cleandoc).split('\n')
"""
import os
+import sys
import unittest
import textwrap
-from . import markdown
+from . import markdown, util
try:
import tidylib
return textwrap.dedent(text).strip()
+class recursionlimit:
+ """
+ A context manager which temporarily modifies the Python recursion limit.
+
+ The testing framework, coverage, etc. may add an arbitrary number of levels to the depth. To maintain consistency
+ in the tests, the current stack depth is determined when called, then added to the provided limit.
+
+ Example usage:
+
+ with recursionlimit(20):
+ # test code here
+
+ See https://stackoverflow.com/a/50120316/866026
+ """
+
+ def __init__(self, limit):
+ self.limit = util._get_stack_depth() + limit
+ self.old_limit = sys.getrecursionlimit()
+
+ def __enter__(self):
+ sys.setrecursionlimit(self.limit)
+
+ def __exit__(self, type, value, tb):
+ sys.setrecursionlimit(self.old_limit)
+
+
#########################
# Legacy Test Framework #
#########################
lst = self.__processPlaceholders(
self.__handleInline(text), child
)
- for l in lst:
- self.parent_map[l[0]] = child
+ for item in lst:
+ self.parent_map[item[0]] = child
stack += lst
insertQueue.append((child, lst))
self.ancestors.pop()
import warnings
import xml.etree.ElementTree
from .pep562 import Pep562
+from itertools import count
try:
from importlib import metadata
return text
+def _get_stack_depth(size=2):
+ """Get stack size for caller's frame.
+ See https://stackoverflow.com/a/47956089/866026
+ """
+ frame = sys._getframe(size)
+
+ for size in count(size):
+ frame = frame.f_back
+ if not frame:
+ return size
+
+
+def nearing_recursion_limit():
+ """Return true if current stack depth is withing 100 of maximum limit."""
+ return sys.getrecursionlimit() - _get_stack_depth() < 100
+
+
"""
MISC AUXILIARY CLASSES
=============================================================================
- Test Tools: test_tools.md
- Contributing to Python-Markdown: contributing.md
- Change Log: change_log/index.md
+ - Release Notes for v.3.3: change_log/release-3.3.md
- Release Notes for v.3.2: change_log/release-3.2.md
- Release Notes for v.3.1: change_log/release-3.1.md
- Release Notes for v.3.0: change_log/release-3.0.md
[metadata]
license_file = LICENSE.md
-
-[egg_info]
-tag_build =
-tag_date = 0
-
# conflict with the perl implimentation (which uses "markdown").
SCRIPT_NAME = 'markdown_py'
-
-long_description = '''
-This is a Python implementation of John Gruber's Markdown_.
-It is almost completely compliant with the reference implementation,
-though there are a few known issues. See Features_ for information
-on what exactly is supported and what is not. Additional features are
-supported by the `Available Extensions`_.
-
-.. _Markdown: https://daringfireball.net/projects/markdown/
-.. _Features: https://Python-Markdown.github.io#features
-.. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
-
-Support
-=======
-
-You may report bugs, ask for help, and discuss various other issues on
-the `bug tracker`_.
-
-.. _`bug tracker`: https://github.com/Python-Markdown/markdown/issues
-'''
-
+with open('README.md') as f:
+ long_description = f.read()
setup(
name='Markdown',
version=__version__,
url='https://Python-Markdown.github.io/',
download_url='http://pypi.python.org/packages/source/M/Markdown/Markdown-%s-py2.py3-none-any.whl' % __version__,
+ project_urls={
+ 'Documentation': 'https://Python-Markdown.github.io/',
+ 'GitHub Project': 'https://github.com/Python-Markdown/markdown',
+ 'Issue Tracker': 'https://github.com/Python-Markdown/markdown/issues'
+ },
description='Python implementation of Markdown.',
long_description=long_description,
+ long_description_content_type='text/markdown',
author='Manfred Stienstra, Yuri takhteyev and Waylan limberg',
author_email='waylan.limberg@icloud.com',
maintainer='Waylan Limberg',
maintainer_email='waylan.limberg@icloud.com',
license='BSD License',
packages=['markdown', 'markdown.extensions'],
- python_requires='>=3.5',
- install_requires=["importlib_metadata;python_version<'3.8'"],
+ python_requires='>=3.6',
+ install_requires=["importlib-metadata;python_version<'3.8'"],
extras_require={
'testing': [
'coverage',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Documentation',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Text Processing :: Filters',
- 'Topic :: Text Processing :: Markup :: HTML'
+ 'Topic :: Text Processing :: Markup :: HTML',
+ 'Topic :: Text Processing :: Markup :: Markdown'
]
)
+++ /dev/null
-<p>Simple block on one line:</p>
-<div>foo</div>
-
-<p>And nested without indentation:</p>
-<div>
-<div>
-<div>
-foo
-</div>
-</div>
-<div>bar</div>
-</div>
\ No newline at end of file
+++ /dev/null
-Simple block on one line:
-
-<div>foo</div>
-
-And nested without indentation:
-
-<div>
-<div>
-<div>
-foo
-</div>
-</div>
-<div>bar</div>
-</div>
+++ /dev/null
-<p>Paragraph one.</p>
-<!-- This is a simple comment -->
-
-<!--
- This is another comment.
--->
-
-<p>Paragraph two.</p>
-<!-- one comment block -- -- with two comments -->
-
-<p>The end.</p>
\ No newline at end of file
+++ /dev/null
-Paragraph one.
-
-<!-- This is a simple comment -->
-
-<!--
- This is another comment.
--->
-
-Paragraph two.
-
-<!-- one comment block -- -- with two comments -->
-
-The end.
+++ /dev/null
-<p>Here's a simple block:</p>
-<div>
- foo
-</div>
-
-<p>This should be a code block, though:</p>
-<pre><code><div>
- foo
-</div>
-</code></pre>
-<p>As should this:</p>
-<pre><code><div>foo</div>
-</code></pre>
-<p>Now, nested:</p>
-<div>
- <div>
- <div>
- foo
- </div>
- </div>
-</div>
-
-<p>This should just be an HTML comment:</p>
-<!-- Comment -->
-
-<p>Multiline:</p>
-<!--
-Blah
-Blah
--->
-
-<p>Code block:</p>
-<pre><code><!-- Comment -->
-</code></pre>
-<p>Just plain comment, with trailing spaces on the line:</p>
-<!-- foo -->
-
-<p>Code:</p>
-<pre><code><hr />
-</code></pre>
-<p>Hr's:</p>
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr class="foo" id="bar" />
-
-<hr class="foo" id="bar"/>
-
-<hr class="foo" id="bar" >
-
-<p><some <a href="http://example.com">weird</a> stuff></p>
-<p><some>> <<unbalanced>> <<brackets></p>
\ No newline at end of file
+++ /dev/null
-Here's a simple block:
-
-<div>
- foo
-</div>
-
-This should be a code block, though:
-
- <div>
- foo
- </div>
-
-As should this:
-
- <div>foo</div>
-
-Now, nested:
-
-<div>
- <div>
- <div>
- foo
- </div>
- </div>
-</div>
-
-This should just be an HTML comment:
-
-<!-- Comment -->
-
-Multiline:
-
-<!--
-Blah
-Blah
--->
-
-Code block:
-
- <!-- Comment -->
-
-Just plain comment, with trailing spaces on the line:
-
-<!-- foo -->
-
-Code:
-
- <hr />
-
-Hr's:
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr class="foo" id="bar" />
-
-<hr class="foo" id="bar"/>
-
-<hr class="foo" id="bar" >
-
-<some [weird](http://example.com) stuff>
-
-<some>> <<unbalanced>> <<brackets>
\ No newline at end of file
+++ /dev/null
-<p>An <abbr title="Abbreviation">ABBR</abbr>: "<abbr title="Reference">REF</abbr>".
-ref and REFERENCE should be ignored.</p>
-<p>The <abbr title="Hyper Text Markup Language">HTML</abbr> specification
-is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>.</p>
\ No newline at end of file
+++ /dev/null
-An ABBR: "REF".
-ref and REFERENCE should be ignored.
-
-*[REF]: Reference
-*[ABBR]: This gets overriden by the next one.
-*[ABBR]: Abbreviation
-
-The HTML specification
-is maintained by the W3C.
-
-*[HTML]: Hyper Text Markup Language
-*[W3C]: World Wide Web Consortium
-
</div>
<p>The tail of the <code>DefaultBlockMode</code> subelement.</p>
<p name="DefaultSpanMode">
-This text <em>is not</em> wrapped in additional <code>p</code> tags.</p>
+This text <em>is not</em> wrapped in additional <code>p</code> tags.
+</p>
<p>The tail of the <code>DefaultSpanMode</code> subelement.</p>
<div name="SpanModeOverride">
This <code>div</code> block is not wrapped in paragraph tags.
-Note: Subelements are not required to have tail text.</div>
+Note: Subelements are not required to have tail text.
+</div>
<p name="BlockModeOverride">
<p>This <code>p</code> block <em>is</em> foolishly wrapped in further paragraph tags.</p>
</p>
<div name="RawHtml">
Raw html blocks may also be nested.
</div>
-
</div>
<p>This text is after the markdown in html.</p>
<div name="issue308">
<div name="RawHtml">
Raw html blocks may also be nested.
</div>
-
<p>Markdown is <em>still</em> active here.</p>
</div>
<p>Markdown is <em>active again</em> here.</p>
<div>
<p>foo bar</p>
-<p><em>bar</em>
-</p>
+<p><em>bar</em></p>
</div>
<div name="issue584">
<div>
+++ /dev/null
-<table>
-<thead>
-<tr>
-<th>First Header</th>
-<th>Second Header</th>
-</tr>
-</thead>
-<tbody>
-<tr>
-<td class="foo bar" title="Some title!">Content Cell</td>
-<td>Content Cell</td>
-</tr>
-<tr>
-<td>Content Cell</td>
-<td class="foo bar" title="Some title!">Content Cell</td>
-</tr>
-</tbody>
-</table>
\ No newline at end of file
+++ /dev/null
-First Header | Second Header
------------------------------------------------------- | -------------
-Content Cell{: class="foo bar" title="Some title!" } | Content Cell
-Content Cell | Content Cell{: class="foo bar" title="Some title!" }
+++ /dev/null
-<p>index 0000000..6e956a9</p>
-<pre><code>--- /dev/null
-+++ b/test/data/stripped_text/mike-30-lili
-@@ -0,0 +1,27 @@
-+Summary:
-+ drift_mod.py | 1 +
-+ 1 files changed, 1 insertions(+), 0 deletions(-)
-+
-+commit da4bfb04debdd994683740878d09988b2641513d
-+Author: Mike Dirolf <mike@dirolf.com>
-+Date: Tue Jan 17 13:42:28 2012 -0500
-+
-+```
-+minor: just wanted to push something.
-+```
-+
-+diff --git a/drift_mod.py b/drift_mod.py
-+index 34dfba6..8a88a69 100644
-+
-+```
-+--- a/drift_mod.py
-++++ b/drift_mod.py
-+@@ -281,6 +281,7 @@ CONTEXT_DIFF_LINE_PATTERN = re.compile(r'^('
-+ '|\+ .*'
-+ '|- .*'
-+ ')$')
-++
-+ def wrap_context_diffs(message_text):
-+ return _wrap_diff(CONTEXT_DIFF_HEADER_PATTERN,
-+ CONTEXT_DIFF_LINE_PATTERN,
-+```
-</code></pre>
\ No newline at end of file
+++ /dev/null
-index 0000000..6e956a9
-
-```
---- /dev/null
-+++ b/test/data/stripped_text/mike-30-lili
-@@ -0,0 +1,27 @@
-+Summary:
-+ drift_mod.py | 1 +
-+ 1 files changed, 1 insertions(+), 0 deletions(-)
-+
-+commit da4bfb04debdd994683740878d09988b2641513d
-+Author: Mike Dirolf <mike@dirolf.com>
-+Date: Tue Jan 17 13:42:28 2012 -0500
-+
-+```
-+minor: just wanted to push something.
-+```
-+
-+diff --git a/drift_mod.py b/drift_mod.py
-+index 34dfba6..8a88a69 100644
-+
-+```
-+--- a/drift_mod.py
-++++ b/drift_mod.py
-+@@ -281,6 +281,7 @@ CONTEXT_DIFF_LINE_PATTERN = re.compile(r'^('
-+ '|\+ .*'
-+ '|- .*'
-+ ')$')
-++
-+ def wrap_context_diffs(message_text):
-+ return _wrap_diff(CONTEXT_DIFF_HEADER_PATTERN,
-+ CONTEXT_DIFF_LINE_PATTERN,
-+```
-```
<p>index 0000000..6e956a9</p>
-<pre><code class="diff">--- /dev/null
+<pre><code class="language-diff">--- /dev/null
+++ b/test/data/stripped_text/mike-30-lili
@@ -0,0 +1,27 @@
+Summary:
+ CONTEXT_DIFF_LINE_PATTERN,
+```
</code></pre>
-
<p>Test support for foo+bar lexer names.</p>
-<pre><code class="html+jinja"><title>{% block title %}{% endblock %}</title>
+<pre><code class="language-html+jinja"><title>{% block title %}{% endblock %}</title>
<ul>
{% for user in users %}
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
+++ /dev/null
-<p>&</p>
-<p>AT&T</p>
\ No newline at end of file
+++ /dev/null
-&
-
-AT&T
-
-
--- /dev/null
+construction:0.000000:0.000000
+CRLF_line_ends:0.020000:0.000000
+adjacent-headers:0.020000:0.000000
+amp-in-url:0.020000:0.000000
+ampersand:0.010000:0.000000
+arabic:0.110000:0.000000
+attributes2:0.020000:0.000000
+bidi:0.310000:0.000000
+blank-block-quote:0.010000:0.000000
+blockquote:0.090000:0.000000
+blockquote-hr:0.060000:0.000000
+bold_links:0.020000:0.000000
+br:0.040000:0.000000
+bracket_re:7.490000:0.000000
+code-first-line:0.020000:0.000000
+comments:0.040000:0.000000
+div:0.040000:0.000000
+email:0.030000:0.000000
+funky-list:0.060000:0.000000
+h1:0.040000:0.000000
+hash:0.040000:0.000000
+headers:0.060000:0.000000
+hline:0.020000:0.000000
+html:0.090000:0.000000
+image:0.040000:0.000000
+image-2:0.060000:0.000000
+image_in_links:0.060000:0.000000
+inside_html:0.040000:0.000000
+japanese:0.150000:0.000000
+lazy-block-quote:0.050000:0.000000
+lists:0.110000:0.000000
+lists2:0.040000:0.000000
+lists3:0.040000:0.000000
+lists4:0.050000:0.000000
+lists5:0.070000:0.000000
+markup-inside-p:0.080000:0.000000
+mismatched-tags:0.070000:0.000000
+more_comments:0.060000:0.000000
+multi-line-tags:0.080000:0.000000
+multi-paragraph-block-quote:0.070000:0.000000
+multi-test:0.150000:0.000000
+multiline-comments:0.090000:0.000000
+normalize:0.060000:0.000000
+numeric-entity:0.090000:0.000000
+php:0.080000:0.000000
+pre:0.080000:0.000000
+russian:0.200000:0.000000
+some-test:0.200000:0.000000
+span:0.140000:0.000000
+strong-with-underscores:0.090000:0.000000
+stronintags:0.160000:0.000000
+tabs-in-lists:0.170000:0.000000
+two-spaces:0.160000:0.000000
+uche:0.150000:0.000000
+underscores:0.120000:0.000000
+url_spaces:0.120000:0.000000
+++ /dev/null
-<section>
- <header>
- <hgroup>
- <h1>Hello :-)</h1>
- </hgroup>
- </header>
- <figure>
- <img src="image.png" alt="" />
- <figcaption>Caption</figcaption>
- </figure>
- <footer>
- <p>Some footer</p>
- </footer>
-</section>
-
-<figure></figure>
\ No newline at end of file
+++ /dev/null
-<section>
- <header>
- <hgroup>
- <h1>Hello :-)</h1>
- </hgroup>
- </header>
- <figure>
- <img src="image.png" alt="" />
- <figcaption>Caption</figcaption>
- </figure>
- <footer>
- <p>Some footer</p>
- </footer>
-</section><figure></figure>
+++ /dev/null
-<blockquote>
-Raw HTML processing should not confuse this with the blockquote below
-</blockquote>
-
-<div id="current-content">
- <div id="primarycontent" class="hfeed">
- <div id="post-">
- <div class="page-head">
- <h2>Header2</h2>
- </div>
- <div class="entry-content">
- <h3>Header3</h3>
- <p>Paragraph</p>
- <h3>Header3</h3>
- <p>Paragraph</p>
- <blockquote>
- <p>Paragraph</p>
- </blockquote>
- <p>Paragraph</p>
- <p><a href="/somelink">linktext</a></p>
- </div>
- </div><!-- #post-ID -->
- <!-- add contact form here -->
- </div><!-- #primarycontent -->
-</div>
-
-<!-- #current-content -->
\ No newline at end of file
+++ /dev/null
-<blockquote>
-Raw HTML processing should not confuse this with the blockquote below
-</blockquote>
-<div id="current-content">
- <div id="primarycontent" class="hfeed">
- <div id="post-">
- <div class="page-head">
- <h2>Header2</h2>
- </div>
- <div class="entry-content">
- <h3>Header3</h3>
- <p>Paragraph</p>
- <h3>Header3</h3>
- <p>Paragraph</p>
- <blockquote>
- <p>Paragraph</p>
- </blockquote>
- <p>Paragraph</p>
- <p><a href="/somelink">linktext</a></p>
- </div>
- </div><!-- #post-ID -->
- <!-- add contact form here -->
- </div><!-- #primarycontent -->
-</div><!-- #current-content -->
+++ /dev/null
-<p>foo</p>
-
-<ul>
-<li>
-<p>bar</p>
-</li>
-<li>
-<p>baz</p>
-</li>
-</ul>
\ No newline at end of file
+++ /dev/null
-<p>foo</p>
-<ul>
-<li>
-<p>bar</p>
-</li>
-<li>
-<p>baz</p>
-</li>
-</ul>
+++ /dev/null
-<p>X<0</p>
-<p>X>0</p>
-<!-- A comment -->
-
-<div>as if</div>
-
-<!-- comment -->
-
-<p><strong>no blank line</strong></p>
\ No newline at end of file
+++ /dev/null
-X<0
-
-X>0
-
-<!-- A comment -->
-
-<div>as if</div>
-
-<!-- comment -->
-__no blank line__
+++ /dev/null
-<div id="sidebar">
-
- _foo_
-
-</div>
-
-<p>And now in uppercase:</p>
-<DIV>
-foo
-</DIV>
\ No newline at end of file
+++ /dev/null
-<div id="sidebar">
-
- _foo_
-
-</div>
-
-And now in uppercase:
-
-<DIV>
-foo
-</DIV>
+++ /dev/null
-<p>Here is HTML <!-- **comment** -->
-and once more <p><!--comment--></p></p>
\ No newline at end of file
+++ /dev/null
-Here is HTML <!-- **comment** -->
-and once more <p><!--comment--></p>
+++ /dev/null
-<h1>Block level html</h1>
-
-<p>Some inline <b>stuff<b>. </p>
-<p>Now some <arbitrary>arbitrary tags</arbitrary>.</p>
-<div>More block level html.</div>
-
-<div class="foo bar" title="with 'quoted' text." valueless_attr weirdness="<i>foo</i>">
-Html with various attributes.
-</div>
-
-<div>
- <div>
- Div with a blank line
-
- in the middle.
- </div>
- <div>
- This gets treated as HTML.
- </div>
-</div>
-
-<p>And of course <script>blah</script>.</p>
-<p><a href="<script>stuff</script>">this <script>link</a></p>
-<p>Some funky <x\]> inline stuff with markdown escaping syntax.</p>
-<p><img scr="foo.png" title="Only one inline element on a line." /></p>
-<p>And now a line with only an opening bracket:</p>
-<p><</p>
-<p>And one with other stuff but no closing bracket:</p>
-<p>< foo</p>
\ No newline at end of file
+++ /dev/null
-
-<h1>Block level html</h1>
-
-Some inline <b>stuff<b>.
-
-Now some <arbitrary>arbitrary tags</arbitrary>.
-
-<div>More block level html.</div>
-
-<div class="foo bar" title="with 'quoted' text." valueless_attr weirdness="<i>foo</i>">
-Html with various attributes.
-</div>
-
-<div>
- <div>
- Div with a blank line
-
- in the middle.
- </div>
- <div>
- This gets treated as HTML.
- </div>
-</div>
-
-And of course <script>blah</script>.
-
-[this <script>link](<script>stuff</script>)
-
-Some funky <x\]> inline stuff with markdown escaping syntax.
-
-<img scr="foo.png" title="Only one inline element on a line." />
-
-And now a line with only an opening bracket:
-
-<
-
-And one with other stuff but no closing bracket:
-
-< foo
-
+++ /dev/null
-<p>
-
-_foo_
-
-</p>
-
-<p>
-_foo_
-</p>
-
-<p>_foo_</p>
-
-<p>
-
-_foo_
-</p>
-
-<p>
-_foo_
-
-</p>
\ No newline at end of file
+++ /dev/null
-<p>
-
-_foo_
-
-</p>
-
-<p>
-_foo_
-</p>
-
-<p>_foo_</p>
-
-<p>
-
-_foo_
-</p>
-
-<p>
-_foo_
-
-</p>
+++ /dev/null
-<p>Some text</p>
-
-<div>some more text</div>
-
-<p>and a bit more</p>
-<p>And this output</p>
-
-<p><em>Compatible with PHP Markdown Extra 1.2.2 and Markdown.pl1.0.2b8:</em></p>
-<!-- comment -->
-
-<p><div>text</div><br /></p>
-
-<p><br /></p>
-<p>Should be in p</p>
\ No newline at end of file
+++ /dev/null
-<p>Some text</p><div>some more text</div>
-
-and a bit more
-
-<p>And this output</p> *Compatible with PHP Markdown Extra 1.2.2 and Markdown.pl1.0.2b8:*
-
-<!-- comment --><p><div>text</div><br /></p><br />
-
-Should be in p
+++ /dev/null
-<!asd@asdfd.com>
-
-<p>Foo</p>
-<p><asd!@asdfd.com></p>
-<p>Bar</p>
-<!--asd@asdfd.com>
-
-Still in unclosed comment
\ No newline at end of file
+++ /dev/null
-<!asd@asdfd.com>
-
-Foo
-
-<asd!@asdfd.com>
-
-Bar
-
-<!--asd@asdfd.com>
-
-Still in unclosed comment
+++ /dev/null
-<div>
-
-asdf asdfasd
-
-</div>
-
-<div>
-
-foo bar
-
-</div>
-
-<p>No blank line.</p>
\ No newline at end of file
+++ /dev/null
-
-<div>
-
-asdf asdfasd
-
-</div>
-
-<div>
-
-foo bar
-
-</div>
-No blank line.
+++ /dev/null
-<!--
-
-foo
-
--->
-
-<p>
-
-foo
-
-</p>
-
-<div>
-
-foo
-
-</div>
-
-<!-- foo
-
--->
-
-<!-- <tag>
-
--->
-
-<!--
-
-foo -->
-
-<!--
-
-<tag> -->
-
-<!-- unclosed comment
-
-__Still__ a comment (browsers see it that way)
\ No newline at end of file
+++ /dev/null
-<!--
-
-foo
-
--->
-
-<p>
-
-foo
-
-</p>
-
-
-<div>
-
-foo
-
-</div>
-
-<!-- foo
-
--->
-
-<!-- <tag>
-
--->
-
-<!--
-
-foo -->
-
-<!--
-
-<tag> -->
-
-<!-- unclosed comment
-
-__Still__ a comment (browsers see it that way)
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/html4/strict.dtd">
-
-<p><b>This should have a p tag</b></p>
-<!--This is a comment -->
-
-<div>This shouldn't</div>
-
-<?php echo "block_level";?>
-
-<p><?php echo "not_block_level";?></p>
\ No newline at end of file
+++ /dev/null
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/html4/strict.dtd">
-
-<b>This should have a p tag</b>
-
-<!--This is a comment -->
-
-<div>This shouldn't</div>
-
-<?php echo "block_level";?>
-
- <?php echo "not_block_level";?>
-
+++ /dev/null
-<pre>
-
-aaa
-
-bbb
-</pre>
-
-<pre>
-* and this is pre-formatted content
-* and it should be printed just like this
-* and not formatted as a list
-
-</pre>
\ No newline at end of file
+++ /dev/null
-<pre>
-
-aaa
-
-bbb
-</pre>
-
-<pre>
-* and this is pre-formatted content
-* and it should be printed just like this
-* and not formatted as a list
-
-</pre>
-
+++ /dev/null
-<p>Preserve whitespace in raw html</p>
-<pre>
-class Foo():
- bar = 'bar'
-
- def baz(self):
- print self.bar
-</pre>
\ No newline at end of file
+++ /dev/null
-Preserve whitespace in raw html
-
-<pre>
-class Foo():
- bar = 'bar'
-
- def baz(self):
- print self.bar
-</pre>
-
--- /dev/null
+<HTTP://WWW.SOMEURL.COM>
+
+<hr@company.com>
\ No newline at end of file
--- /dev/null
+<p><a href="HTTP://WWW.SOMEURL.COM">HTTP://WWW.SOMEURL.COM</a></p>
+
+<p><a href="mailto:hr@company.com">hr@company.com</a></p>
\ No newline at end of file
--- /dev/null
+Tricky combinaisons:\r\rbackslash with \\-- two dashes\r\rbackslash with \\> greater than\r\r\\\[test](not a link)\r\r\\\*no emphasis*
\ No newline at end of file
--- /dev/null
+<p>Tricky combinaisons:</p>\r\r<p>backslash with \-- two dashes</p>\r\r<p>backslash with \> greater than</p>\r\r<p>\[test](not a link)</p>\r\r<p>\*no emphasis*</p>\r
\ No newline at end of file
--- /dev/null
+From `<!--` to `-->`
+on two lines.
+
+From `<!--`
+to `-->`
+on three lines.
--- /dev/null
+<p>From <code><!--</code> to <code>--></code>
+on two lines.</p>
+
+<p>From <code><!--</code>
+to <code>--></code>
+on three lines.</p>
--- /dev/null
+
+* List Item:
+
+ code block
+
+ with a blank line
+
+ within a list item.
+
+* code block
+ as first element of a list item
+
+* List Item:
+
+ code block with whitespace on preceding line
\ No newline at end of file
--- /dev/null
+<ul>
+<li><p>List Item:</p>
+
+<pre><code>code block
+
+with a blank line
+</code></pre>
+
+<p>within a list item.</p></li>
+<li><pre><code>code block
+as first element of a list item
+</code></pre></li>
+
+<li><p>List Item:</p>
+
+<pre><code>code block with whitespace on preceding line
+</code></pre></li>
+</ul>
\ No newline at end of file
--- /dev/null
+
+ Codeblock on second line
--- /dev/null
+<pre><code>Codeblock on second line
+</code></pre>
--- /dev/null
+<michel.fortin@michelf.com>
+
+International domain names: <help@tūdaliņ.lv>
\ No newline at end of file
--- /dev/null
+<p><a href="mailto:michel.fortin@michelf.com">michel.fortin@michelf.com</a></p>
+
+<p>International domain names: <a href="mailto:help@tūdaliņ.lv">help@tūdaliņ.lv</a></p>
--- /dev/null
+Combined emphasis:
+
+1. ***test test***
+2. ___test test___
+3. *test **test***
+4. **test *test***
+5. ***test* test**
+6. ***test** test*
+7. ***test* test**
+8. **test *test***
+9. *test **test***
+10. _test __test___
+11. __test _test___
+12. ___test_ test__
+13. ___test__ test_
+14. ___test_ test__
+15. __test _test___
+16. _test __test___
+
+
+Incorrect nesting:
+
+1. *test **test* test**
+2. _test __test_ test__
+3. **test *test** test*
+4. __test _test__ test_
+5. *test *test* test*
+6. _test _test_ test_
+7. **test **test** test**
+8. __test __test__ test__
+
+
+
+No emphasis:
+
+1. test* test *test
+2. test** test **test
+3. test_ test _test
+4. test__ test __test
+
+
+
+Middle-word emphasis (asterisks):
+
+1. *a*b
+2. a*b*
+3. a*b*c
+4. **a**b
+5. a**b**
+6. a**b**c
+
+
+Middle-word emphasis (underscore):
+
+1. _a_b
+2. a_b_
+3. a_b_c
+4. __a__b
+5. a__b__
+6. a__b__c
+
+my_precious_file.txt
+
+
+## Tricky Cases
+
+E**. **Test** TestTestTest
+
+E**. **Test** Test Test Test
+
+
+## Overlong emphasis
+
+Name: ____________
+Organization: ____
+Region/Country: __
+
+_____Cut here_____
+
+____Cut here____
--- /dev/null
+<p>Combined emphasis:</p>
+
+<ol>
+<li><strong><em>test test</em></strong></li>
+<li><strong><em>test test</em></strong></li>
+<li><em>test <strong>test</strong></em></li>
+<li><strong>test <em>test</em></strong></li>
+<li><strong><em>test</em> test</strong></li>
+<li><em><strong>test</strong> test</em></li>
+<li><strong><em>test</em> test</strong></li>
+<li><strong>test <em>test</em></strong></li>
+<li><em>test <strong>test</strong></em></li>
+<li><em>test <strong>test</strong></em></li>
+<li><strong>test <em>test</em></strong></li>
+<li><strong><em>test</em> test</strong></li>
+<li><em><strong>test</strong> test</em></li>
+<li><strong><em>test</em> test</strong></li>
+<li><strong>test <em>test</em></strong></li>
+<li><em>test <strong>test</strong></em></li>
+</ol>
+
+<p>Incorrect nesting:</p>
+
+<ol>
+<li>*test <strong>test* test</strong></li>
+<li>_test <strong>test_ test</strong></li>
+<li><strong>test *test</strong> test*</li>
+<li><strong>test _test</strong> test_</li>
+<li><em>test *test</em> test*</li>
+<li><em>test _test</em> test_</li>
+<li><strong>test **test</strong> test**</li>
+<li><strong>test __test</strong> test__</li>
+</ol>
+
+<p>No emphasis:</p>
+
+<ol>
+<li>test* test *test</li>
+<li>test** test **test</li>
+<li>test_ test _test</li>
+<li>test__ test __test</li>
+</ol>
+
+<p>Middle-word emphasis (asterisks):</p>
+
+<ol>
+<li><em>a</em>b</li>
+<li>a<em>b</em></li>
+<li>a<em>b</em>c</li>
+<li><strong>a</strong>b</li>
+<li>a<strong>b</strong></li>
+<li>a<strong>b</strong>c</li>
+</ol>
+
+<p>Middle-word emphasis (underscore):</p>
+
+<ol>
+<li><em>a</em>b</li>
+<li>a<em>b</em></li>
+<li>a<em>b</em>c</li>
+<li><strong>a</strong>b</li>
+<li>a<strong>b</strong></li>
+<li>a<strong>b</strong>c</li>
+</ol>
+
+<p>my<em>precious</em>file.txt</p>
+
+<h2>Tricky Cases</h2>
+
+<p>E**. <strong>Test</strong> TestTestTest</p>
+
+<p>E**. <strong>Test</strong> Test Test Test</p>
+
+
+<h2>Overlong emphasis</h2>
+
+<p>Name: ____________<br />
+Organization: ____<br />
+Region/Country: __</p>
+
+<p>_____Cut here_____</p>
+
+<p>____Cut here____</p>
\ No newline at end of file
--- /dev/null
+With asterisks
+
+ * List item
+ *
+ * List item
+
+With numbers
+
+1. List item
+2.
+3. List item
+
+With hyphens
+
+- List item
+-
+- List item
+
+With asterisks
+
+ * List item
+ * List item
+ *
+
+With numbers
+
+1. List item
+2. List item
+3.
+
+With hyphens
+
+- List item
+- List item
+-
--- /dev/null
+<p>With asterisks</p>
+
+<ul>
+<li>List item</li>
+<li></li>
+<li>List item</li>
+</ul>
+
+<p>With numbers</p>
+
+<ol>
+<li>List item</li>
+<li></li>
+<li>List item</li>
+</ol>
+
+<p>With hyphens</p>
+
+<ul>
+<li>List item</li>
+<li></li>
+<li>List item</li>
+</ul>
+
+<p>With asterisks</p>
+
+<ul>
+<li>List item</li>
+<li>List item</li>
+<li></li>
+</ul>
+
+<p>With numbers</p>
+
+<ol>
+<li>List item</li>
+<li>List item</li>
+<li></li>
+</ol>
+
+<p>With hyphens</p>
+
+<ul>
+<li>List item</li>
+<li>List item</li>
+<li></li>
+</ul>
\ No newline at end of file
--- /dev/null
+Header\r======\r\rHeader\r------\r\r### Header
+
+ - - -
+
+Header\r======\rParagraph\r\rHeader\r------\rParagraph\r\r### Header\rParagraph
+
+ - - -
+
+Paragraph\rHeader\r======\rParagraph\r\rParagraph\rHeader\r------\rParagraph\r\rParagraph\r### Header\rParagraph
\ No newline at end of file
--- /dev/null
+<h1>Header</h1>
+
+<h2>Header</h2>
+
+<h3>Header</h3>
+
+<hr />
+
+<h1>Header</h1>
+
+<p>Paragraph</p>
+
+<h2>Header</h2>
+
+<p>Paragraph</p>
+
+<h3>Header</h3>
+
+<p>Paragraph</p>
+
+<hr />
+
+<p>Paragraph</p>
+
+<h1>Header</h1>
+
+<p>Paragraph</p>
+
+<p>Paragraph</p>
+
+<h2>Header</h2>
+
+<p>Paragraph</p>
+
+<p>Paragraph</p>
+
+<h3>Header</h3>
+
+<p>Paragraph</p>
--- /dev/null
+Horizontal rules:
+
+- - -
+
+* * *
+
+***
+
+---
+
+___
+
+Not horizontal rules (testing for a bug in 1.0.1j):
+
++++
+
+,,,
+
+===
+
+???
+
+AAA
+
+jjj
+
+j j j
+
+n n n
--- /dev/null
+<p>Horizontal rules:</p>
+
+<hr />
+
+<hr />
+
+<hr />
+
+<hr />
+
+<hr />
+
+<p>Not horizontal rules (testing for a bug in 1.0.1j):</p>
+
+<p>+++</p>
+
+<p>,,,</p>
+
+<p>===</p>
+
+<p>???</p>
+
+<p>AAA</p>
+
+<p>jjj</p>
+
+<p>j j j</p>
+
+<p>n n n</p>
+
--- /dev/null
+With some attributes:
+
+<div id="test">
+ foo
+</div>
+
+<div id="test"
+ class="nono">
+ foo
+</div>
+
+Hr's:
+
+<hr class="foo"
+ id="bar" >
--- /dev/null
+<p>With some attributes:</p>
+
+<div id="test">
+ foo
+</div>
+
+<div id="test"
+ class="nono">
+ foo
+</div>
+
+<p>Hr's:</p>
+
+<hr class="foo"
+ id="bar" >
\ No newline at end of file
--- /dev/null
+<abbr title="` **Attribute Content Is Not A Code Span** `">ACINACS</abbr>
+
+<abbr title="`first backtick!">SB</abbr>
+<abbr title="`second backtick!">SB</abbr>
\ No newline at end of file
--- /dev/null
+<p><abbr title="` **Attribute Content Is Not A Code Span** `">ACINACS</abbr></p>
+
+<p><abbr title="`first backtick!">SB</abbr>
+<abbr title="`second backtick!">SB</abbr></p>
\ No newline at end of file
--- /dev/null
+Paragraph one.
+
+<!-- double--dash (invalid SGML comment) -->
+
+Paragraph two.
+
+<!-- enclosed tag </div> -->
+
+The end.
--- /dev/null
+<p>Paragraph one.</p>
+
+<!-- double--dash (invalid SGML comment) -->
+
+<p>Paragraph two.</p>
+
+<!-- enclosed tag </div> -->
+
+<p>The end.</p>
--- /dev/null
+Here is a block tag ins:
+
+<ins>
+<p>Some text</p>
+</ins>
+
+<ins>And here it is inside a paragraph.</ins>
+
+And here it is <ins>in the middle of</ins> a paragraph.
+
+<del>
+<p>Some text</p>
+</del>
+
+<del>And here is ins as a paragraph.</del>
+
+And here it is <del>in the middle of</del> a paragraph.
--- /dev/null
+<p>Here is a block tag ins:</p>
+
+<ins>
+<p>Some text</p>
+</ins>
+
+<p><ins>And here it is inside a paragraph.</ins></p>
+
+<p>And here it is <ins>in the middle of</ins> a paragraph.</p>
+
+<del>
+<p>Some text</p>
+</del>
+
+<p><del>And here is ins as a paragraph.</del></p>
+
+<p>And here it is <del>in the middle of</del> a paragraph.</p>
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
--- /dev/null
+[silly URL w/ angle brackets](<?}]*+|&)>).
--- /dev/null
+<p><a href="?}]*+|&)">silly URL w/ angle brackets</a>.</p>
--- /dev/null
+# Character Escapes
+
+The MD5 value for `+` is "26b17225b626fb9238849fd60eabdf60".
+
+# HTML Blocks
+
+<p>test</p>
+
+The MD5 value for `<p>test</p>` is:
+
+6205333b793f34273d75379350b36826
\ No newline at end of file
--- /dev/null
+<h1>Character Escapes</h1>
+
+<p>The MD5 value for <code>+</code> is "26b17225b626fb9238849fd60eabdf60".</p>
+
+<h1>HTML Blocks</h1>
+
+<p>test</p>
+
+<p>The MD5 value for <code><p>test</p></code> is:</p>
+
+<p>6205333b793f34273d75379350b36826</p>
--- /dev/null
+* test
++ test
+- test
+
+1. test
+2. test
+
+* test
++ test
+- test
+
+1. test
+2. test
--- /dev/null
+<ul>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+</ul>
+
+<ol>
+<li>test</li>
+<li>test</li>
+</ol>
+
+<ul>
+<li>test</li>
+<li>test</li>
+<li>test</li>
+</ul>
+
+<ol>
+<li>test</li>
+<li>test</li>
+</ol>
--- /dev/null
+Valid nesting:
+
+**[Link](url)**
+
+[**Link**](url)
+
+**[**Link**](url)**
+
+Invalid nesting:
+
+[[Link](url)](url)
\ No newline at end of file
--- /dev/null
+<p>Valid nesting:</p>
+
+<p><strong><a href="url">Link</a></strong></p>
+
+<p><a href="url"><strong>Link</strong></a></p>
+
+<p><strong><a href="url"><strong>Link</strong></a></strong></p>
+
+<p>Invalid nesting:</p>
+
+<p><a href="url">[Link](url)</a></p>
--- /dev/null
+This tests for a bug where quotes escaped by PHP when using
+`preg_replace` with the `/e` modifier must be correctly unescaped
+(hence the `_UnslashQuotes` function found only in PHP Markdown).
+
+
+
+Headers below should appear exactly as they are typed (no backslash
+added or removed).
+
+Header "quoted\" again \\""
+===========================
+
+Header "quoted\" again \\""
+---------------------------
+
+### Header "quoted\" again \\"" ###
+
+
+
+Test with tabs for `_Detab`:
+
+ Code 'block' with some "tabs" and "quotes"
--- /dev/null
+<p>This tests for a bug where quotes escaped by PHP when using
+<code>preg_replace</code> with the <code>/e</code> modifier must be correctly unescaped
+(hence the <code>_UnslashQuotes</code> function found only in PHP Markdown).</p>
+
+<p>Headers below should appear exactly as they are typed (no backslash
+added or removed).</p>
+
+<h1>Header "quoted\" again \""</h1>
+
+<h2>Header "quoted\" again \""</h2>
+
+<h3>Header "quoted\" again \""</h3>
+
+<p>Test with tabs for <code>_Detab</code>:</p>
+
+<pre><code>Code 'block' with some "tabs" and "quotes"
+</code></pre>
--- /dev/null
+[Inline link 1 with parens](/url\(test\) "title").
+
+[Inline link 2 with parens](</url\(test\)> "title").
+
+[Inline link 3 with non-escaped parens](/url(test) "title").
+
+[Inline link 4 with non-escaped parens](</url(test)> "title").
+
+[Reference link 1 with parens][1].
+
+[Reference link 2 with parens][2].
+
+ [1]: /url(test) "title"
+ [2]: </url(test)> "title"
--- /dev/null
+<p><a href="/url(test)" title="title">Inline link 1 with parens</a>.</p>
+
+<p><a href="/url(test)" title="title">Inline link 2 with parens</a>.</p>
+
+<p><a href="/url(test)" title="title">Inline link 3 with non-escaped parens</a>.</p>
+
+<p><a href="/url(test)" title="title">Inline link 4 with non-escaped parens</a>.</p>
+
+<p><a href="/url(test)" title="title">Reference link 1 with parens</a>.</p>
+
+<p><a href="/url(test)" title="title">Reference link 2 with parens</a>.</p>
\ No newline at end of file
--- /dev/null
+[Test](/"style="color:red)
+[Test](/'style='color:red)
+
+
+
--- /dev/null
+<p><a href="/"style="color:red">Test</a>
+<a href="/'style='color:red">Test</a></p>
+
+<p><img src="/"style="border-color:red;border-size:1px;border-style:solid" alt="" />
+<img src="/'style='border-color:red;border-size:1px;border-style:solid" alt="" /></p>
--- /dev/null
+Paragraph and no space:\r* ciao\r\rParagraph and 1 space:\r * ciao\r\rParagraph and 3 spaces:\r * ciao\r\rParagraph and 4 spaces:\r * ciao\r\rParagraph before header:\r#Header\r\rParagraph before blockquote:\r>Some quote.\r
\ No newline at end of file
--- /dev/null
+<p>Paragraph and no space:
+* ciao</p>
+
+<p>Paragraph and 1 space:
+ * ciao</p>
+
+<p>Paragraph and 3 spaces:
+ * ciao</p>
+
+<p>Paragraph and 4 spaces:
+ * ciao</p>
+
+<p>Paragraph before header:</p>
+
+<h1>Header</h1>
+
+<p>Paragraph before blockquote:</p>
+
+<blockquote>
+ <p>Some quote.</p>
+</blockquote>
--- /dev/null
+Some text about HTML, SGML and HTML4.
+
+Let's talk about the U.S.A., (É.U. or É.-U. d'A. in French).
+
+*[HTML4]: Hyper Text Markup Language version 4
+*[HTML]: Hyper Text Markup Language
+*[SGML]: Standard Generalized Markup Language
+*[U.S.A.]: United States of America
+*[É.U.] : États-Unis d'Amérique
+*[É.-U. d'A.] : États-Unis d'Amérique
+
+And here we have a CD, some CDs, and some other CD's.
+
+*[CD]: Compact Disk
+
+Let's transfert documents through TCP/IP, using TCP packets.
+
+*[IP]: Internet Protocol
+*[TCP]: Transmission Control Protocol
+
+ ---
+
+Bienvenue sur [CMS](http://www.bidulecms.com "Bidule CMS").
+
+*[CMS]: Content Management System
+
+ ---
+
+ATCCE
+
+*[ATCCE]: Abbreviation "Testing" Correct 'Character' < Escapes >
\ No newline at end of file
--- /dev/null
+<p>Some text about <abbr title="Hyper Text Markup Language">HTML</abbr>, <abbr title="Standard Generalized Markup Language">SGML</abbr> and <abbr title="Hyper Text Markup Language version 4">HTML4</abbr>.</p>
+
+<p>Let's talk about the <abbr title="United States of America">U.S.A.</abbr>, (<abbr title="États-Unis d'Amérique">É.U.</abbr> or <abbr title="États-Unis d'Amérique">É.-U. d'A.</abbr> in French).</p>
+
+<p>And here we have a <abbr title="Compact Disk">CD</abbr>, some CDs, and some other <abbr title="Compact Disk">CD</abbr>'s.</p>
+
+<p>Let's transfert documents through <abbr title="Transmission Control Protocol">TCP</abbr>/<abbr title="Internet Protocol">IP</abbr>, using <abbr title="Transmission Control Protocol">TCP</abbr> packets.</p>
+
+<hr />
+
+<p>Bienvenue sur <a href="http://www.bidulecms.com" title="Bidule CMS"><abbr title="Content Management System">CMS</abbr></a>.</p>
+
+<hr />
+
+<p><abbr title="Abbreviation "Testing" Correct 'Character' < Escapes >">ATCCE</abbr></p>
--- /dev/null
+A simple definition list:
+
+Term 1
+: Definition 1
+
+Term 2
+: Definition 2
+
+With multiple terms:
+
+Term 1
+Term 2
+: Definition 1
+
+Term 3
+Term 4
+: Definition 2
+
+With multiple definitions:
+
+Term 1
+: Definition 1
+: Definition 2
+
+Term 2
+: Definition 3
+: Definition 4
+
+With multiple lines per definition:
+
+Term 1
+: Definition 1 line 1 ...
+Definition 1 line 2
+: Definition 2 line 1 ...
+Definition 2 line 2
+
+Term 2
+: Definition 3 line 2 ...
+ Definition 3 line 2
+: Definition 4 line 2 ...
+ Definition 4 line 2
+
+With paragraphs:
+
+Term 1
+
+: Definition 1 (paragraph)
+
+Term 2
+
+: Definition 2 (paragraph)
+
+With multiple paragraphs:
+
+Term 1
+
+: Definition 1 paragraph 1 line 1 ...
+ Definition 1 paragraph 1 line 2
+
+ Definition 1 paragraph 2 line 1 ...
+ Definition 1 paragraph 2 line 2
+
+Term 2
+
+: Definition 1 paragraph 1 line 1 ...
+Definition 1 paragraph 1 line 2 (lazy)
+
+ Definition 1 paragraph 2 line 1 ...
+Definition 1 paragraph 2 line 2 (lazy)
+
+* * *
+
+A mix:
+
+Term 1
+Term 2
+
+: Definition 1 paragraph 1 line 1 ...
+Definition 1 paragraph 1 line 2 (lazy)
+
+ Definition 1 paragraph 2 line 1 ...
+ Definition 1 paragraph 2 line 2
+
+: Definition 2 paragraph 1 line 1 ...
+Definition 2 paragraph 1 line 2 (lazy)
+
+Term 3
+: Definition 3 (no paragraph)
+: Definition 4 (no paragraph)
+: Definition 5 line 1 ...
+ Definition 5 line 2 (no paragraph)
+
+: Definition 6 paragraph 1 line 1 ...
+Definition 6 paragraph 1 line 2
+: Definition 7 (no paragraph)
+: Definition 8 paragraph 1 line 1 (forced paragraph) ...
+ Definition 8 paragraph 1 line 2
+
+ Definition 8 paragraph 2 line 1
+
+Term 4
+: Definition 9 paragraph 1 line 1 (forced paragraph) ...
+ Definition 9 paragraph 1 line 2
+
+ Definition 9 paragraph 2 line 1
+: Definition 10 (no paragraph)
+
+* * *
+
+Special cases:
+
+Term
+
+: code block
+ as first element of a definition
\ No newline at end of file
--- /dev/null
+<p>A simple definition list:</p>
+
+<dl>
+<dt>Term 1</dt>
+<dd>Definition 1</dd>
+
+<dt>Term 2</dt>
+<dd>Definition 2</dd>
+</dl>
+
+<p>With multiple terms:</p>
+
+<dl>
+<dt>Term 1</dt>
+<dt>Term 2</dt>
+<dd>Definition 1</dd>
+
+<dt>Term 3</dt>
+<dt>Term 4</dt>
+<dd>Definition 2</dd>
+</dl>
+
+<p>With multiple definitions:</p>
+
+<dl>
+<dt>Term 1</dt>
+<dd>Definition 1</dd>
+
+<dd>Definition 2</dd>
+
+<dt>Term 2</dt>
+<dd>Definition 3</dd>
+
+<dd>Definition 4</dd>
+</dl>
+
+<p>With multiple lines per definition:</p>
+
+<dl>
+<dt>Term 1</dt>
+<dd>Definition 1 line 1 ...
+Definition 1 line 2</dd>
+
+<dd>Definition 2 line 1 ...
+Definition 2 line 2</dd>
+
+<dt>Term 2</dt>
+<dd>Definition 3 line 2 ...
+Definition 3 line 2</dd>
+
+<dd>Definition 4 line 2 ...
+Definition 4 line 2</dd>
+</dl>
+
+<p>With paragraphs:</p>
+
+<dl>
+<dt>Term 1</dt>
+<dd>
+<p>Definition 1 (paragraph)</p>
+</dd>
+
+<dt>Term 2</dt>
+<dd>
+<p>Definition 2 (paragraph)</p>
+</dd>
+</dl>
+
+<p>With multiple paragraphs:</p>
+
+<dl>
+<dt>Term 1</dt>
+<dd>
+<p>Definition 1 paragraph 1 line 1 ...
+Definition 1 paragraph 1 line 2</p>
+
+<p>Definition 1 paragraph 2 line 1 ...
+Definition 1 paragraph 2 line 2</p>
+</dd>
+
+<dt>Term 2</dt>
+<dd>
+<p>Definition 1 paragraph 1 line 1 ...
+Definition 1 paragraph 1 line 2 (lazy)</p>
+
+<p>Definition 1 paragraph 2 line 1 ...
+Definition 1 paragraph 2 line 2 (lazy)</p>
+</dd>
+</dl>
+
+<hr />
+
+<p>A mix:</p>
+
+<dl>
+<dt>Term 1</dt>
+<dt>Term 2</dt>
+<dd>
+<p>Definition 1 paragraph 1 line 1 ...
+Definition 1 paragraph 1 line 2 (lazy)</p>
+
+<p>Definition 1 paragraph 2 line 1 ...
+Definition 1 paragraph 2 line 2</p>
+</dd>
+
+<dd>
+<p>Definition 2 paragraph 1 line 1 ...
+Definition 2 paragraph 1 line 2 (lazy)</p>
+</dd>
+
+<dt>Term 3</dt>
+<dd>Definition 3 (no paragraph)</dd>
+
+<dd>Definition 4 (no paragraph)</dd>
+
+<dd>Definition 5 line 1 ...
+Definition 5 line 2 (no paragraph)</dd>
+
+<dd>
+<p>Definition 6 paragraph 1 line 1 ...
+Definition 6 paragraph 1 line 2</p>
+</dd>
+
+<dd>Definition 7 (no paragraph)</dd>
+
+<dd>
+<p>Definition 8 paragraph 1 line 1 (forced paragraph) ...
+Definition 8 paragraph 1 line 2</p>
+
+<p>Definition 8 paragraph 2 line 1</p>
+</dd>
+
+<dt>Term 4</dt>
+<dd>
+<p>Definition 9 paragraph 1 line 1 (forced paragraph) ...
+Definition 9 paragraph 1 line 2</p>
+
+<p>Definition 9 paragraph 2 line 1</p>
+</dd>
+
+<dd>Definition 10 (no paragraph)</dd>
+</dl>
+
+<hr />
+
+<p>Special cases:</p>
+
+<dl>
+<dt>Term</dt>
+<dd>
+<pre><code>code block
+as first element of a definition
+</code></pre>
+</dd>
+</dl>
--- /dev/null
+Combined emphasis:
+
+1. ***test test***
+2. ___test test___
+3. *test **test***
+4. **test *test***
+5. ***test* test**
+6. ***test** test*
+7. ***test* test**
+8. **test *test***
+9. *test **test***
+10. _test __test___
+11. __test _test___
+12. ___test_ test__
+13. ___test__ test_
+14. ___test_ test__
+15. __test _test___
+16. _test __test___
+
+
+Incorrect nesting:
+
+1. *test **test* test**
+2. _test __test_ test__
+3. **test *test** test*
+4. __test _test__ test_
+5. *test *test* test*
+6. _test _test_ test_
+7. **test **test** test**
+8. __test __test__ test__
+
+
+
+No emphasis:
+
+1. test* test *test
+2. test** test **test
+3. test_ test _test
+4. test__ test __test
+
+
+
+Middle-word emphasis (asterisks):
+
+1. *a*b
+2. a*b*
+3. a*b*c
+4. **a**b
+5. a**b**
+6. a**b**c
+
+
+Middle-word emphasis (underscore):
+
+1. _a_b
+2. a_b_
+3. a_b_c
+4. __a__b
+5. a__b__
+6. a__b__c
+
+my_precious_file.txt
+
+
+## Tricky Cases
+
+E**. **Test** TestTestTest
+
+E**. **Test** Test Test Test
+
+
+## Overlong emphasis
+
+Name: ____________
+Organization: ____
+Region/Country: __
+
+_____Cut here_____
+
+____Cut here____
--- /dev/null
+<p>Combined emphasis:</p>
+
+<ol>
+<li><strong><em>test test</em></strong></li>
+<li><strong><em>test test</em></strong></li>
+<li><em>test <strong>test</strong></em></li>
+<li><strong>test <em>test</em></strong></li>
+<li><strong><em>test</em> test</strong></li>
+<li><em><strong>test</strong> test</em></li>
+<li><strong><em>test</em> test</strong></li>
+<li><strong>test <em>test</em></strong></li>
+<li><em>test <strong>test</strong></em></li>
+<li><em>test <strong>test</strong></em></li>
+<li><strong>test <em>test</em></strong></li>
+<li><strong><em>test</em> test</strong></li>
+<li><em><strong>test</strong> test</em></li>
+<li><strong><em>test</em> test</strong></li>
+<li><strong>test <em>test</em></strong></li>
+<li><em>test <strong>test</strong></em></li>
+</ol>
+
+<p>Incorrect nesting:</p>
+
+<ol>
+<li>*test <strong>test* test</strong></li>
+<li>_test <strong>test_ test</strong></li>
+<li><strong>test *test</strong> test*</li>
+<li><strong>test _test</strong> test_</li>
+<li><em>test *test</em> test*</li>
+<li><em>test _test</em> test_</li>
+<li><strong>test **test</strong> test**</li>
+<li><strong>test __test</strong> test__</li>
+</ol>
+
+<p>No emphasis:</p>
+
+<ol>
+<li>test* test *test</li>
+<li>test** test **test</li>
+<li>test_ test _test</li>
+<li>test__ test __test</li>
+</ol>
+
+<p>Middle-word emphasis (asterisks):</p>
+
+<ol>
+<li><em>a</em>b</li>
+<li>a<em>b</em></li>
+<li>a<em>b</em>c</li>
+<li><strong>a</strong>b</li>
+<li>a<strong>b</strong></li>
+<li>a<strong>b</strong>c</li>
+</ol>
+
+<p>Middle-word emphasis (underscore):</p>
+
+<ol>
+<li>_a_b</li>
+<li>a_b_</li>
+<li>a_b_c</li>
+<li>__a__b</li>
+<li>a__b__</li>
+<li>a__b__c</li>
+</ol>
+
+<p>my_precious_file.txt</p>
+
+<h2>Tricky Cases</h2>
+
+<p>E**. <strong>Test</strong> TestTestTest</p>
+
+<p>E**. <strong>Test</strong> Test Test Test</p>
+
+
+<h2>Overlong emphasis</h2>
+
+<p>Name: ____________<br />
+Organization: ____<br />
+Region/Country: __</p>
+
+<p>_____Cut here_____</p>
+
+<p>____Cut here____</p>
--- /dev/null
+~~~
+Fenced
+~~~
+
+Code block starting and ending with empty lines:
+~~~
+
+
+Fenced
+
+
+~~~
+
+Indented code block containing fenced code block sample:
+
+ ~~~
+ Fenced
+ ~~~
+
+Fenced code block with indented code block sample:
+
+~~~
+Some text
+
+ Indented code block sample code
+~~~
+
+Fenced code block with long markers:
+
+~~~~~~~~~~~~~~~~~~
+Fenced
+~~~~~~~~~~~~~~~~~~
+
+Fenced code block with fenced code block markers of different length in it:
+
+~~~~
+In code block
+~~~
+Still in code block
+~~~~~
+Still in code block
+~~~~
+
+Fenced code block with Markdown header and horizontal rule:
+
+~~~
+#test
+---
+~~~
+
+Fenced code block with link definitions, footnote definition and
+abbreviation definitions:
+
+~~~
+[example]: http://example.com/
+
+[^1]: Footnote def
+
+*[HTML]: HyperText Markup Language
+~~~
\ No newline at end of file
--- /dev/null
+<pre><code>Fenced
+</code></pre>
+
+<p>Code block starting and ending with empty lines:</p>
+
+<pre><code><br /><br />Fenced
+
+
+</code></pre>
+
+<p>Indented code block containing fenced code block sample:</p>
+
+<pre><code>~~~
+Fenced
+~~~
+</code></pre>
+
+<p>Fenced code block with indented code block sample:</p>
+
+<pre><code>Some text
+
+ Indented code block sample code
+</code></pre>
+
+<p>Fenced code block with long markers:</p>
+
+<pre><code>Fenced
+</code></pre>
+
+<p>Fenced code block with fenced code block markers of different length in it:</p>
+
+<pre><code>In code block
+~~~
+Still in code block
+~~~~~
+Still in code block
+</code></pre>
+
+<p>Fenced code block with Markdown header and horizontal rule:</p>
+
+<pre><code>#test
+---
+</code></pre>
+
+<p>Fenced code block with link definitions, footnote definition and
+abbreviation definitions:</p>
+
+<pre><code>[example]: http://example.com/
+
+[^1]: Footnote def
+
+*[HTML]: HyperText Markup Language
+</code></pre>
--- /dev/null
+This is the first paragraph.[^first]
+
+[^first]: This is the first note.
+
+* List item one.[^second]
+* List item two.[^third]
+
+[^third]: This is the third note, defined out of order.
+[^second]: This is the second note.
+[^fourth]: This is the fourth note.
+
+# Header[^fourth]
+
+Some paragraph with a footnote[^1], and another[^2].
+
+[^1]: Content for fifth footnote.
+[^2]: Content for sixth footnote spaning on
+ three lines, with some span-level markup like
+ _emphasis_, a [link][].
+
+[link]: http://www.michelf.com/
+
+Another paragraph with a named footnote[^fn-name].
+
+[^fn-name]:
+ Footnote beginning on the line next to the marker.
+
+This paragraph should not have a footnote marker since
+the footnote is undefined.[^3]
+
+This paragraph should not have a footnote marker since
+the footnote has already been used before.[^1]
+
+This paragraph links to a footnote with plenty of
+block-level content.[^block]
+
+[^block]:
+ Paragraph.
+
+ * List item
+
+ > Blockquote
+
+ Code block
+
+This paragraph host the footnote reference within a
+footnote test[^reference].
+
+[^reference]:
+ This footnote has a footnote of its own.[^nested]
+
+[^nested]:
+ This footnote should appear even though as it is refered
+ from another footnote. But [^reference] should be litteral
+ since the footnote with that name has already been used.
+
+ - - -
+
+Testing unusual footnote name[^1$^!"'].
+
+[^1$^!"']: Haha!
--- /dev/null
+<p>This is the first paragraph.<sup id="fnref:first"><a href="#fn:first" rel="footnote">1</a></sup></p>
+
+<ul>
+<li>List item one.<sup id="fnref:second"><a href="#fn:second" rel="footnote">2</a></sup></li>
+<li>List item two.<sup id="fnref:third"><a href="#fn:third" rel="footnote">3</a></sup></li>
+</ul>
+
+<h1>Header<sup id="fnref:fourth"><a href="#fn:fourth" rel="footnote">4</a></sup></h1>
+
+<p>Some paragraph with a footnote<sup id="fnref:1"><a href="#fn:1" rel="footnote">5</a></sup>, and another<sup id="fnref:2"><a href="#fn:2" rel="footnote">6</a></sup>.</p>
+
+<p>Another paragraph with a named footnote<sup id="fnref:fn-name"><a href="#fn:fn-name" rel="footnote">7</a></sup>.</p>
+
+<p>This paragraph should not have a footnote marker since
+the footnote is undefined.[^3]</p>
+
+<p>This paragraph should not have a footnote marker since
+the footnote has already been used before.[^1]</p>
+
+<p>This paragraph links to a footnote with plenty of
+block-level content.<sup id="fnref:block"><a href="#fn:block" rel="footnote">8</a></sup></p>
+
+<p>This paragraph host the footnote reference within a
+footnote test<sup id="fnref:reference"><a href="#fn:reference" rel="footnote">9</a></sup>.</p>
+
+<hr />
+
+<p>Testing unusual footnote name<sup id="fnref:1$^!"'"><a href="#fn:1$^!"'" rel="footnote">10</a></sup>.</p>
+
+<div class="footnotes">
+<hr />
+<ol>
+
+<li id="fn:first">
+<p>This is the first note. <a href="#fnref:first" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:second">
+<p>This is the second note. <a href="#fnref:second" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:third">
+<p>This is the third note, defined out of order. <a href="#fnref:third" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:fourth">
+<p>This is the fourth note. <a href="#fnref:fourth" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:1">
+<p>Content for fifth footnote. <a href="#fnref:1" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:2">
+<p>Content for sixth footnote spaning on
+three lines, with some span-level markup like
+<em>emphasis</em>, a <a href="http://www.michelf.com/">link</a>. <a href="#fnref:2" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:fn-name">
+<p>Footnote beginning on the line next to the marker. <a href="#fnref:fn-name" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:block">
+<p>Paragraph.</p>
+
+<ul>
+<li>List item</li>
+</ul>
+
+<blockquote>
+ <p>Blockquote</p>
+</blockquote>
+
+<pre><code>Code block
+</code></pre>
+
+<p><a href="#fnref:block" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:reference">
+<p>This footnote has a footnote of its own.<sup id="fnref:nested"><a href="#fn:nested" rel="footnote">11</a></sup> <a href="#fnref:reference" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:1$^!"'">
+<p>Haha! <a href="#fnref:1$^!"'" rev="footnote">↩</a></p>
+</li>
+
+<li id="fn:nested">
+<p>This footnote should appear even though as it is refered
+from another footnote. But [^reference] should be litteral
+since the footnote with that name has already been used. <a href="#fnref:nested" rev="footnote">↩</a></p>
+</li>
+
+</ol>
+</div>
--- /dev/null
+# Markdown inside code blocks
+
+<div markdown="1">
+foo
+</div>
+
+<div markdown='1'>
+foo
+</div>
+
+<div markdown=1>
+foo
+</div>
+
+<table>
+<tr><td markdown="1">test _emphasis_ (span)</td></tr>
+</table>
+
+<table>
+<tr><td markdown="span">test _emphasis_ (span)</td></tr>
+</table>
+
+<table>
+<tr><td markdown="block">test _emphasis_ (block)</td></tr>
+</table>
+
+## More complicated
+
+<table>
+<tr><td markdown="1">
+* this is _not_ a list item</td></tr>
+<tr><td markdown="span">
+* this is _not_ a list item</td></tr>
+<tr><td markdown="block">
+* this _is_ a list item
+</td></tr>
+</table>
+
+## With indent
+
+<div>
+ <div markdown="1">
+ This text is no code block: if it was, the
+ closing `<div>` would be too and the HTML block
+ would be invalid.
+
+ Markdown content in HTML blocks is assumed to be
+ indented the same as the block opening tag.
+
+ **This should be the third paragraph after the header.**
+ </div>
+</div>
+
+## Code block with rogue `</div>`s in Markdown code span and block
+
+<div>
+ <div markdown="1">
+
+ This is a code block however:
+
+ </div>
+
+ Funny isn't it? Here is a code span: `</div>`.
+
+ </div>
+</div>
+
+<div>
+ <div markdown="1">
+ * List item, not a code block
+
+Some text
+
+ This is a code block.
+ </div>
+</div>
+
+## No code block in markdown span mode
+
+<p markdown="1">
+ This is not a code block since Markdown parse paragraph
+ content as span. Code spans like `</p>` are allowed though.
+</p>
+
+<p markdown="1">_Hello_ _world_</p>
+
+## Preserving attributes and tags on more than one line:
+
+<p class="test" markdown="1"
+id="12">
+Some _span_ content.
+</p>
+
+
+## Header confusion bug
+
+<table class="canvas">
+<tr>
+<td id="main" markdown="1">Hello World!
+============
+
+Hello World!</td>
+</tr>
+</table>
--- /dev/null
+<h1>Markdown inside code blocks</h1>
+
+<div>
+
+<p>foo</p>
+
+</div>
+
+<div>
+
+<p>foo</p>
+
+</div>
+
+<div>
+
+<p>foo</p>
+
+</div>
+
+<table>
+<tr><td>test <em>emphasis</em> (span)</td></tr>
+</table>
+
+<table>
+<tr><td>test <em>emphasis</em> (span)</td></tr>
+</table>
+
+<table>
+<tr><td>
+
+<p>test <em>emphasis</em> (block)</p>
+
+</td></tr>
+</table>
+
+<h2>More complicated</h2>
+
+<table>
+<tr><td>
+* this is <em>not</em> a list item</td></tr>
+<tr><td>
+* this is <em>not</em> a list item</td></tr>
+<tr><td>
+
+<ul>
+<li>this <em>is</em> a list item</li>
+</ul>
+
+</td></tr>
+</table>
+
+<h2>With indent</h2>
+
+<div>
+ <div>
+
+<p>This text is no code block: if it was, the
+closing <code><div></code> would be too and the HTML block
+would be invalid.</p>
+
+<p>Markdown content in HTML blocks is assumed to be
+indented the same as the block opening tag.</p>
+
+<p><strong>This should be the third paragraph after the header.</strong></p>
+
+</div>
+</div>
+
+<h2>Code block with rogue <code></div></code>s in Markdown code span and block</h2>
+
+<div>
+ <div>
+
+<p>This is a code block however:</p>
+
+<pre><code></div>
+</code></pre>
+
+<p>Funny isn't it? Here is a code span: <code></div></code>.</p>
+
+</div>
+</div>
+
+<div>
+ <div>
+
+<ul>
+<li>List item, not a code block</li>
+</ul>
+
+<p>Some text</p>
+
+<pre><code>This is a code block.
+</code></pre>
+
+</div>
+</div>
+
+<h2>No code block in markdown span mode</h2>
+
+<p>
+ This is not a code block since Markdown parse paragraph
+ content as span. Code spans like <code></p></code> are allowed though.
+</p>
+
+<p><em>Hello</em> <em>world</em></p>
+
+<h2>Preserving attributes and tags on more than one line:</h2>
+
+<p class="test"
+id="12">
+Some <em>span</em> content.
+</p>
+
+<h2>Header confusion bug</h2>
+
+<table class="canvas">
+<tr>
+<td id="main">Hello World!
+============
+
+Hello World!</td>
+</tr>
+</table>
\ No newline at end of file
--- /dev/null
+# Simple tables
+
+Header 1 | Header 2
+--------- | ---------
+Cell 1 | Cell 2
+Cell 3 | Cell 4
+
+With leading pipes:
+
+| Header 1 | Header 2
+| --------- | ---------
+| Cell 1 | Cell 2
+| Cell 3 | Cell 4
+
+With tailing pipes:
+
+Header 1 | Header 2 |
+--------- | --------- |
+Cell 1 | Cell 2 |
+Cell 3 | Cell 4 |
+
+With leading and tailing pipes:
+
+| Header 1 | Header 2 |
+| --------- | --------- |
+| Cell 1 | Cell 2 |
+| Cell 3 | Cell 4 |
+
+* * *
+
+# One-column one-row table
+
+With leading pipes:
+
+| Header
+| -------
+| Cell
+
+With tailing pipes:
+
+Header |
+------- |
+Cell |
+
+With leading and tailing pipes:
+
+| Header |
+| ------- |
+| Cell |
+
+* * *
+
+Table alignement:
+
+| Default | Right | Center | Left |
+| --------- |:--------- |:---------:| ---------:|
+| Long Cell | Long Cell | Long Cell | Long Cell |
+| Cell | Cell | Cell | Cell |
+
+Table alignement (alternate spacing):
+
+| Default | Right | Center | Left |
+| --------- | :-------- | :-------: | --------: |
+| Long Cell | Long Cell | Long Cell | Long Cell |
+| Cell | Cell | Cell | Cell |
+
+* * *
+
+# Empty cells
+
+| Header 1 | Header 2 |
+| --------- | --------- |
+| A | B |
+| C | |
+
+Header 1 | Header 2
+--------- | ---------
+A | B
+ | D
+
+* * *
+
+# Missing tailing pipe
+
+Header 1 | Header 2
+--------- | --------- |
+Cell | Cell |
+Cell | Cell |
+
+Header 1 | Header 2 |
+--------- | ---------
+Cell | Cell |
+Cell | Cell |
+
+Header 1 | Header 2 |
+--------- | --------- |
+Cell | Cell
+Cell | Cell |
+
+Header 1 | Header 2 |
+--------- | --------- |
+Cell | Cell |
+Cell | Cell
+
--- /dev/null
+<h1>Simple tables</h1>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell 1</td>
+ <td>Cell 2</td>
+</tr>
+<tr>
+ <td>Cell 3</td>
+ <td>Cell 4</td>
+</tr>
+</tbody>
+</table>
+
+<p>With leading pipes:</p>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell 1</td>
+ <td>Cell 2</td>
+</tr>
+<tr>
+ <td>Cell 3</td>
+ <td>Cell 4</td>
+</tr>
+</tbody>
+</table>
+
+<p>With tailing pipes:</p>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell 1</td>
+ <td>Cell 2</td>
+</tr>
+<tr>
+ <td>Cell 3</td>
+ <td>Cell 4</td>
+</tr>
+</tbody>
+</table>
+
+<p>With leading and tailing pipes:</p>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell 1</td>
+ <td>Cell 2</td>
+</tr>
+<tr>
+ <td>Cell 3</td>
+ <td>Cell 4</td>
+</tr>
+</tbody>
+</table>
+
+<hr />
+
+<h1>One-column one-row table</h1>
+
+<p>With leading pipes:</p>
+
+<table>
+<thead>
+<tr>
+ <th>Header</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell</td>
+</tr>
+</tbody>
+</table>
+
+<p>With tailing pipes:</p>
+
+<table>
+<thead>
+<tr>
+ <th>Header</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell</td>
+</tr>
+</tbody>
+</table>
+
+<p>With leading and tailing pipes:</p>
+
+<table>
+<thead>
+<tr>
+ <th>Header</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell</td>
+</tr>
+</tbody>
+</table>
+
+<hr />
+
+<p>Table alignement:</p>
+
+<table>
+<thead>
+<tr>
+ <th>Default</th>
+ <th align="left">Right</th>
+ <th align="center">Center</th>
+ <th align="right">Left</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Long Cell</td>
+ <td align="left">Long Cell</td>
+ <td align="center">Long Cell</td>
+ <td align="right">Long Cell</td>
+</tr>
+<tr>
+ <td>Cell</td>
+ <td align="left">Cell</td>
+ <td align="center">Cell</td>
+ <td align="right">Cell</td>
+</tr>
+</tbody>
+</table>
+
+<p>Table alignement (alternate spacing):</p>
+
+<table>
+<thead>
+<tr>
+ <th>Default</th>
+ <th align="left">Right</th>
+ <th align="center">Center</th>
+ <th align="right">Left</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Long Cell</td>
+ <td align="left">Long Cell</td>
+ <td align="center">Long Cell</td>
+ <td align="right">Long Cell</td>
+</tr>
+<tr>
+ <td>Cell</td>
+ <td align="left">Cell</td>
+ <td align="center">Cell</td>
+ <td align="right">Cell</td>
+</tr>
+</tbody>
+</table>
+
+<hr />
+
+<h1>Empty cells</h1>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>A</td>
+ <td>B</td>
+</tr>
+<tr>
+ <td>C</td>
+ <td></td>
+</tr>
+</tbody>
+</table>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>A</td>
+ <td>B</td>
+</tr>
+<tr>
+ <td></td>
+ <td>D</td>
+</tr>
+</tbody>
+</table>
+
+<hr />
+
+<h1>Missing tailing pipe</h1>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell</td>
+ <td>Cell</td>
+</tr>
+<tr>
+ <td>Cell</td>
+ <td>Cell</td>
+</tr>
+</tbody>
+</table>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell</td>
+ <td>Cell</td>
+</tr>
+<tr>
+ <td>Cell</td>
+ <td>Cell</td>
+</tr>
+</tbody>
+</table>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell</td>
+ <td>Cell</td>
+</tr>
+<tr>
+ <td>Cell</td>
+ <td>Cell</td>
+</tr>
+</tbody>
+</table>
+
+<table>
+<thead>
+<tr>
+ <th>Header 1</th>
+ <th>Header 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>Cell</td>
+ <td>Cell</td>
+</tr>
+<tr>
+ <td>Cell</td>
+ <td>Cell</td>
+</tr>
+</tbody>
+</table>
\ No newline at end of file
--- /dev/null
+AT&T has an ampersand in their name.
+
+AT&T is another way to write it.
+
+This & that.
+
+4 < 5.
+
+6 > 5.
+
+Here's a [link] [1] with an ampersand in the URL.
+
+Here's a link with an amersand in the link text: [AT&T] [2].
+
+Here's an inline [link](/script?foo=1&bar=2).
+
+Here's an inline [link](</script?foo=1&bar=2>).
+
+
+[1]: http://example.com/?foo=1&bar=2
+[2]: http://att.com/ "AT&T"
\ No newline at end of file
--- /dev/null
+Link: <http://example.com/>.
+
+With an ampersand: <http://example.com/?foo=1&bar=2>
+
+* In a list?
+* <http://example.com/>
+* It should.
+
+> Blockquoted: <http://example.com/>
+
+Auto-links should not occur here: `<http://example.com/>`
+
+ or here: <http://example.com/>
\ No newline at end of file
--- /dev/null
+These should all get escaped:
+
+Backslash: \\
+
+Backtick: \`
+
+Asterisk: \*
+
+Underscore: \_
+
+Left brace: \{
+
+Right brace: \}
+
+Left bracket: \[
+
+Right bracket: \]
+
+Left paren: \(
+
+Right paren: \)
+
+Greater-than: \>
+
+Hash: \#
+
+Period: \.
+
+Bang: \!
+
+Plus: \+
+
+Minus: \-
+
+
+
+These should not, because they occur within a code block:
+
+ Backslash: \\
+
+ Backtick: \`
+
+ Asterisk: \*
+
+ Underscore: \_
+
+ Left brace: \{
+
+ Right brace: \}
+
+ Left bracket: \[
+
+ Right bracket: \]
+
+ Left paren: \(
+
+ Right paren: \)
+
+ Greater-than: \>
+
+ Hash: \#
+
+ Period: \.
+
+ Bang: \!
+
+ Plus: \+
+
+ Minus: \-
+
+
+Nor should these, which occur in code spans:
+
+Backslash: `\\`
+
+Backtick: `` \` ``
+
+Asterisk: `\*`
+
+Underscore: `\_`
+
+Left brace: `\{`
+
+Right brace: `\}`
+
+Left bracket: `\[`
+
+Right bracket: `\]`
+
+Left paren: `\(`
+
+Right paren: `\)`
+
+Greater-than: `\>`
+
+Hash: `\#`
+
+Period: `\.`
+
+Bang: `\!`
+
+Plus: `\+`
+
+Minus: `\-`
--- /dev/null
+> Example:
+>
+> sub status {
+> print "working";
+> }
+>
+> Or:
+>
+> sub status {
+> return "working";
+> }
--- /dev/null
+In Markdown 1.0.0 and earlier. Version
+8. This line turns into a list item.
+Because a hard-wrapped line in the
+middle of a paragraph looked like a
+list item.
+
+Here's one with a bullet.
+* criminey.
--- /dev/null
+Dashes:
+
+---
+
+ ---
+
+ ---
+
+ ---
+
+ ---
+
+- - -
+
+ - - -
+
+ - - -
+
+ - - -
+
+ - - -
+
+
+Asterisks:
+
+***
+
+ ***
+
+ ***
+
+ ***
+
+ ***
+
+* * *
+
+ * * *
+
+ * * *
+
+ * * *
+
+ * * *
+
+
+Underscores:
+
+___
+
+ ___
+
+ ___
+
+ ___
+
+ ___
+
+_ _ _
+
+ _ _ _
+
+ _ _ _
+
+ _ _ _
+
+ _ _ _
--- /dev/null
+Simple block on one line:
+
+<div>foo</div>
+
+And nested without indentation:
+
+<div>
+<div>
+<div>
+foo
+</div>
+</div>
+<div>bar</div>
+</div>
--- /dev/null
+Here's a simple block:
+
+<div>
+ foo
+</div>
+
+This should be a code block, though:
+
+ <div>
+ foo
+ </div>
+
+As should this:
+
+ <div>foo</div>
+
+Now, nested:
+
+<div>
+ <div>
+ <div>
+ foo
+ </div>
+ </div>
+</div>
+
+This should just be an HTML comment:
+
+<!-- Comment -->
+
+Multiline:
+
+<!--
+Blah
+Blah
+-->
+
+Code block:
+
+ <!-- Comment -->
+
+Just plain comment, with trailing spaces on the line:
+
+<!-- foo -->
+
+Code:
+
+ <hr />
+
+Hr's:
+
+<hr>
+
+<hr/>
+
+<hr />
+
+<hr>
+
+<hr/>
+
+<hr />
+
+<hr class="foo" id="bar" />
+
+<hr class="foo" id="bar"/>
+
+<hr class="foo" id="bar" >
+
--- /dev/null
+Paragraph one.
+
+<!-- This is a simple comment -->
+
+<!--
+ This is another comment.
+-->
+
+Paragraph two.
+
+<!-- one comment block -- -- with two comments -->
+
+The end.
--- /dev/null
+Just a [URL](/url/).
+
+[URL and title](/url/ "title").
+
+[URL and title](/url/ "title preceded by two spaces").
+
+[URL and title](/url/ "title preceded by a tab").
+
+[Empty]().
--- /dev/null
+Foo [bar] [1].
+
+Foo [bar][1].
+
+Foo [bar]
+[1].
+
+[1]: /url/ "Title"
+
+
+With [embedded [brackets]] [b].
+
+
+Indented [once][].
+
+Indented [twice][].
+
+Indented [thrice][].
+
+Indented [four][] times.
+
+ [once]: /url
+
+ [twice]: /url
+
+ [thrice]: /url
+
+ [four]: /url
+
+
+[b]: /url/
--- /dev/null
+Foo [bar][].
+
+Foo [bar](/url/ "Title with "quotes" inside").
+
+
+ [bar]: /url/ "Title with "quotes" inside"
+
--- /dev/null
+Markdown: Basics
+================
+
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a class="selected" title="Markdown Basics">Basics</a></li>
+ <li><a href="/projects/markdown/syntax" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+
+
+Getting the Gist of Markdown's Formatting Syntax
+------------------------------------------------
+
+This page offers a brief overview of what it's like to use Markdown.
+The [syntax page] [s] provides complete, detailed documentation for
+every feature, but Markdown should be very easy to pick up simply by
+looking at a few examples of it in action. The examples on this page
+are written in a before/after style, showing example syntax and the
+HTML output produced by Markdown.
+
+It's also helpful to simply try Markdown out; the [Dingus] [d] is a
+web application that allows you type your own Markdown-formatted text
+and translate it to XHTML.
+
+**Note:** This document is itself written using Markdown; you
+can [see the source for it by adding '.text' to the URL] [src].
+
+ [s]: /projects/markdown/syntax "Markdown Syntax"
+ [d]: /projects/markdown/dingus "Markdown Dingus"
+ [src]: /projects/markdown/basics.text
+
+
+## Paragraphs, Headers, Blockquotes ##
+
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing spaces or tabs is considered
+blank.) Normal paragraphs should not be indented with spaces or tabs.
+
+Markdown offers two styles of headers: *Setext* and *atx*.
+Setext-style headers for `<h1>` and `<h2>` are created by
+"underlining" with equal signs (`=`) and hyphens (`-`), respectively.
+To create an atx-style header, you put 1-6 hash marks (`#`) at the
+beginning of the line -- the number of hashes equals the resulting
+HTML header level.
+
+Blockquotes are indicated using email-style '`>`' angle brackets.
+
+Markdown:
+
+ A First Level Header
+ ====================
+
+ A Second Level Header
+ ---------------------
+
+ Now is the time for all good men to come to
+ the aid of their country. This is just a
+ regular paragraph.
+
+ The quick brown fox jumped over the lazy
+ dog's back.
+
+ ### Header 3
+
+ > This is a blockquote.
+ >
+ > This is the second paragraph in the blockquote.
+ >
+ > ## This is an H2 in a blockquote
+
+
+Output:
+
+ <h1>A First Level Header</h1>
+
+ <h2>A Second Level Header</h2>
+
+ <p>Now is the time for all good men to come to
+ the aid of their country. This is just a
+ regular paragraph.</p>
+
+ <p>The quick brown fox jumped over the lazy
+ dog's back.</p>
+
+ <h3>Header 3</h3>
+
+ <blockquote>
+ <p>This is a blockquote.</p>
+
+ <p>This is the second paragraph in the blockquote.</p>
+
+ <h2>This is an H2 in a blockquote</h2>
+ </blockquote>
+
+
+
+### Phrase Emphasis ###
+
+Markdown uses asterisks and underscores to indicate spans of emphasis.
+
+Markdown:
+
+ Some of these words *are emphasized*.
+ Some of these words _are emphasized also_.
+
+ Use two asterisks for **strong emphasis**.
+ Or, if you prefer, __use two underscores instead__.
+
+Output:
+
+ <p>Some of these words <em>are emphasized</em>.
+ Some of these words <em>are emphasized also</em>.</p>
+
+ <p>Use two asterisks for <strong>strong emphasis</strong>.
+ Or, if you prefer, <strong>use two underscores instead</strong>.</p>
+
+
+
+## Lists ##
+
+Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
+`+`, and `-`) as list markers. These three markers are
+interchangable; this:
+
+ * Candy.
+ * Gum.
+ * Booze.
+
+this:
+
+ + Candy.
+ + Gum.
+ + Booze.
+
+and this:
+
+ - Candy.
+ - Gum.
+ - Booze.
+
+all produce the same output:
+
+ <ul>
+ <li>Candy.</li>
+ <li>Gum.</li>
+ <li>Booze.</li>
+ </ul>
+
+Ordered (numbered) lists use regular numbers, followed by periods, as
+list markers:
+
+ 1. Red
+ 2. Green
+ 3. Blue
+
+Output:
+
+ <ol>
+ <li>Red</li>
+ <li>Green</li>
+ <li>Blue</li>
+ </ol>
+
+If you put blank lines between items, you'll get `<p>` tags for the
+list item text. You can create multi-paragraph list items by indenting
+the paragraphs by 4 spaces or 1 tab:
+
+ * A list item.
+
+ With multiple paragraphs.
+
+ * Another item in the list.
+
+Output:
+
+ <ul>
+ <li><p>A list item.</p>
+ <p>With multiple paragraphs.</p></li>
+ <li><p>Another item in the list.</p></li>
+ </ul>
+
+
+
+### Links ###
+
+Markdown supports two styles for creating links: *inline* and
+*reference*. With both styles, you use square brackets to delimit the
+text you want to turn into a link.
+
+Inline-style links use parentheses immediately after the link text.
+For example:
+
+ This is an [example link](http://example.com/).
+
+Output:
+
+ <p>This is an <a href="http://example.com/">
+ example link</a>.</p>
+
+Optionally, you may include a title attribute in the parentheses:
+
+ This is an [example link](http://example.com/ "With a Title").
+
+Output:
+
+ <p>This is an <a href="http://example.com/" title="With a Title">
+ example link</a>.</p>
+
+Reference-style links allow you to refer to your links by names, which
+you define elsewhere in your document:
+
+ I get 10 times more traffic from [Google][1] than from
+ [Yahoo][2] or [MSN][3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+
+Output:
+
+ <p>I get 10 times more traffic from <a href="http://google.com/"
+ title="Google">Google</a> than from <a href="http://search.yahoo.com/"
+ title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/"
+ title="MSN Search">MSN</a>.</p>
+
+The title attribute is optional. Link names may contain letters,
+numbers and spaces, but are *not* case sensitive:
+
+ I start my morning with a cup of coffee and
+ [The New York Times][NY Times].
+
+ [ny times]: http://www.nytimes.com/
+
+Output:
+
+ <p>I start my morning with a cup of coffee and
+ <a href="http://www.nytimes.com/">The New York Times</a>.</p>
+
+
+### Images ###
+
+Image syntax is very much like link syntax.
+
+Inline (titles are optional):
+
+ 
+
+Reference-style:
+
+ ![alt text][id]
+
+ [id]: /path/to/img.jpg "Title"
+
+Both of the above examples produce the same output:
+
+ <img src="/path/to/img.jpg" alt="alt text" title="Title" />
+
+
+
+### Code ###
+
+In a regular paragraph, you can create code span by wrapping text in
+backtick quotes. Any ampersands (`&`) and angle brackets (`<` or
+`>`) will automatically be translated into HTML entities. This makes
+it easy to use Markdown to write about HTML example code:
+
+ I strongly recommend against using any `<blink>` tags.
+
+ I wish SmartyPants used named entities like `—`
+ instead of decimal-encoded entites like `—`.
+
+Output:
+
+ <p>I strongly recommend against using any
+ <code><blink></code> tags.</p>
+
+ <p>I wish SmartyPants used named entities like
+ <code>&mdash;</code> instead of decimal-encoded
+ entites like <code>&#8212;</code>.</p>
+
+
+To specify an entire block of pre-formatted code, indent every line of
+the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`,
+and `>` characters will be escaped automatically.
+
+Markdown:
+
+ If you want your page to validate under XHTML 1.0 Strict,
+ you've got to put paragraph tags in your blockquotes:
+
+ <blockquote>
+ <p>For example.</p>
+ </blockquote>
+
+Output:
+
+ <p>If you want your page to validate under XHTML 1.0 Strict,
+ you've got to put paragraph tags in your blockquotes:</p>
+
+ <pre><code><blockquote>
+ <p>For example.</p>
+ </blockquote>
+ </code></pre>
--- /dev/null
+Markdown: Syntax
+================
+
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a href="/projects/markdown/basics" title="Markdown Basics">Basics</a></li>
+ <li><a class="selected" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+
+
+* [Overview](#overview)
+ * [Philosophy](#philosophy)
+ * [Inline HTML](#html)
+ * [Automatic Escaping for Special Characters](#autoescape)
+* [Block Elements](#block)
+ * [Paragraphs and Line Breaks](#p)
+ * [Headers](#header)
+ * [Blockquotes](#blockquote)
+ * [Lists](#list)
+ * [Code Blocks](#precode)
+ * [Horizontal Rules](#hr)
+* [Span Elements](#span)
+ * [Links](#link)
+ * [Emphasis](#em)
+ * [Code](#code)
+ * [Images](#img)
+* [Miscellaneous](#misc)
+ * [Backslash Escapes](#backslash)
+ * [Automatic Links](#autolink)
+
+
+**Note:** This document is itself written using Markdown; you
+can [see the source for it by adding '.text' to the URL][src].
+
+ [src]: /projects/markdown/syntax.text
+
+* * *
+
+<h2 id="overview">Overview</h2>
+
+<h3 id="philosophy">Philosophy</h3>
+
+Markdown is intended to be as easy-to-read and easy-to-write as is feasible.
+
+Readability, however, is emphasized above all else. A Markdown-formatted
+document should be publishable as-is, as plain text, without looking
+like it's been marked up with tags or formatting instructions. While
+Markdown's syntax has been influenced by several existing text-to-HTML
+filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4],
+[Grutatext] [5], and [EtText] [6] -- the single biggest source of
+inspiration for Markdown's syntax is the format of plain text email.
+
+ [1]: http://docutils.sourceforge.net/mirror/setext.html
+ [2]: http://www.aaronsw.com/2002/atx/
+ [3]: http://textism.com/tools/textile/
+ [4]: http://docutils.sourceforge.net/rst.html
+ [5]: http://www.triptico.com/software/grutatxt.html
+ [6]: http://ettext.taint.org/doc/
+
+To this end, Markdown's syntax is comprised entirely of punctuation
+characters, which punctuation characters have been carefully chosen so
+as to look like what they mean. E.g., asterisks around a word actually
+look like \*emphasis\*. Markdown lists look like, well, lists. Even
+blockquotes look like quoted passages of text, assuming you've ever
+used email.
+
+
+
+<h3 id="html">Inline HTML</h3>
+
+Markdown's syntax is intended for one purpose: to be used as a
+format for *writing* for the web.
+
+Markdown is not a replacement for HTML, or even close to it. Its
+syntax is very small, corresponding only to a very small subset of
+HTML tags. The idea is *not* to create a syntax that makes it easier
+to insert HTML tags. In my opinion, HTML tags are already easy to
+insert. The idea for Markdown is to make it easy to read, write, and
+edit prose. HTML is a *publishing* format; Markdown is a *writing*
+format. Thus, Markdown's formatting syntax only addresses issues that
+can be conveyed in plain text.
+
+For any markup that is not covered by Markdown's syntax, you simply
+use HTML itself. There's no need to preface it or delimit it to
+indicate that you're switching from Markdown to HTML; you just use
+the tags.
+
+The only restrictions are that block-level HTML elements -- e.g. `<div>`,
+`<table>`, `<pre>`, `<p>`, etc. -- must be separated from surrounding
+content by blank lines, and the start and end tags of the block should
+not be indented with tabs or spaces. Markdown is smart enough not
+to add extra (unwanted) `<p>` tags around HTML block-level tags.
+
+For example, to add an HTML table to a Markdown article:
+
+ This is a regular paragraph.
+
+ <table>
+ <tr>
+ <td>Foo</td>
+ </tr>
+ </table>
+
+ This is another regular paragraph.
+
+Note that Markdown formatting syntax is not processed within block-level
+HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an
+HTML block.
+
+Span-level HTML tags -- e.g. `<span>`, `<cite>`, or `<del>` -- can be
+used anywhere in a Markdown paragraph, list item, or header. If you
+want, you can even use HTML tags instead of Markdown formatting; e.g. if
+you'd prefer to use HTML `<a>` or `<img>` tags instead of Markdown's
+link or image syntax, go right ahead.
+
+Unlike block-level HTML tags, Markdown syntax *is* processed within
+span-level tags.
+
+
+<h3 id="autoescape">Automatic Escaping for Special Characters</h3>
+
+In HTML, there are two characters that demand special treatment: `<`
+and `&`. Left angle brackets are used to start tags; ampersands are
+used to denote HTML entities. If you want to use them as literal
+characters, you must escape them as entities, e.g. `<`, and
+`&`.
+
+Ampersands in particular are bedeviling for web writers. If you want to
+write about 'AT&T', you need to write '`AT&T`'. You even need to
+escape ampersands within URLs. Thus, if you want to link to:
+
+ http://images.google.com/images?num=30&q=larry+bird
+
+you need to encode the URL as:
+
+ http://images.google.com/images?num=30&q=larry+bird
+
+in your anchor tag `href` attribute. Needless to say, this is easy to
+forget, and is probably the single most common source of HTML validation
+errors in otherwise well-marked-up web sites.
+
+Markdown allows you to use these characters naturally, taking care of
+all the necessary escaping for you. If you use an ampersand as part of
+an HTML entity, it remains unchanged; otherwise it will be translated
+into `&`.
+
+So, if you want to include a copyright symbol in your article, you can write:
+
+ ©
+
+and Markdown will leave it alone. But if you write:
+
+ AT&T
+
+Markdown will translate it to:
+
+ AT&T
+
+Similarly, because Markdown supports [inline HTML](#html), if you use
+angle brackets as delimiters for HTML tags, Markdown will treat them as
+such. But if you write:
+
+ 4 < 5
+
+Markdown will translate it to:
+
+ 4 < 5
+
+However, inside Markdown code spans and blocks, angle brackets and
+ampersands are *always* encoded automatically. This makes it easy to use
+Markdown to write about HTML code. (As opposed to raw HTML, which is a
+terrible format for writing about HTML syntax, because every single `<`
+and `&` in your example code needs to be escaped.)
+
+
+* * *
+
+
+<h2 id="block">Block Elements</h2>
+
+
+<h3 id="p">Paragraphs and Line Breaks</h3>
+
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing but spaces or tabs is considered
+blank.) Normal paragraphs should not be indented with spaces or tabs.
+
+The implication of the "one or more consecutive lines of text" rule is
+that Markdown supports "hard-wrapped" text paragraphs. This differs
+significantly from most other text-to-HTML formatters (including Movable
+Type's "Convert Line Breaks" option) which translate every line break
+character in a paragraph into a `<br />` tag.
+
+When you *do* want to insert a `<br />` break tag using Markdown, you
+end a line with two or more spaces, then type return.
+
+Yes, this takes a tad more effort to create a `<br />`, but a simplistic
+"every line break is a `<br />`" rule wouldn't work for Markdown.
+Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l]
+work best -- and look better -- when you format them with hard breaks.
+
+ [bq]: #blockquote
+ [l]: #list
+
+
+
+<h3 id="header">Headers</h3>
+
+Markdown supports two styles of headers, [Setext] [1] and [atx] [2].
+
+Setext-style headers are "underlined" using equal signs (for first-level
+headers) and dashes (for second-level headers). For example:
+
+ This is an H1
+ =============
+
+ This is an H2
+ -------------
+
+Any number of underlining `=`'s or `-`'s will work.
+
+Atx-style headers use 1-6 hash characters at the start of the line,
+corresponding to header levels 1-6. For example:
+
+ # This is an H1
+
+ ## This is an H2
+
+ ###### This is an H6
+
+Optionally, you may "close" atx-style headers. This is purely
+cosmetic -- you can use this if you think it looks better. The
+closing hashes don't even need to match the number of hashes
+used to open the header. (The number of opening hashes
+determines the header level.) :
+
+ # This is an H1 #
+
+ ## This is an H2 ##
+
+ ### This is an H3 ######
+
+
+<h3 id="blockquote">Blockquotes</h3>
+
+Markdown uses email-style `>` characters for blockquoting. If you're
+familiar with quoting passages of text in an email message, then you
+know how to create a blockquote in Markdown. It looks best if you hard
+wrap the text and put a `>` before every line:
+
+ > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+ >
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ > id sem consectetuer libero luctus adipiscing.
+
+Markdown allows you to be lazy and only put the `>` before the first
+line of a hard-wrapped paragraph:
+
+ > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ id sem consectetuer libero luctus adipiscing.
+
+Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
+adding additional levels of `>`:
+
+ > This is the first level of quoting.
+ >
+ > > This is nested blockquote.
+ >
+ > Back to the first level.
+
+Blockquotes can contain other Markdown elements, including headers, lists,
+and code blocks:
+
+ > ## This is a header.
+ >
+ > 1. This is the first list item.
+ > 2. This is the second list item.
+ >
+ > Here's some example code:
+ >
+ > return shell_exec("echo $input | $markdown_script");
+
+Any decent text editor should make email-style quoting easy. For
+example, with BBEdit, you can make a selection and choose Increase
+Quote Level from the Text menu.
+
+
+<h3 id="list">Lists</h3>
+
+Markdown supports ordered (numbered) and unordered (bulleted) lists.
+
+Unordered lists use asterisks, pluses, and hyphens -- interchangably
+-- as list markers:
+
+ * Red
+ * Green
+ * Blue
+
+is equivalent to:
+
+ + Red
+ + Green
+ + Blue
+
+and:
+
+ - Red
+ - Green
+ - Blue
+
+Ordered lists use numbers followed by periods:
+
+ 1. Bird
+ 2. McHale
+ 3. Parish
+
+It's important to note that the actual numbers you use to mark the
+list have no effect on the HTML output Markdown produces. The HTML
+Markdown produces from the above list is:
+
+ <ol>
+ <li>Bird</li>
+ <li>McHale</li>
+ <li>Parish</li>
+ </ol>
+
+If you instead wrote the list in Markdown like this:
+
+ 1. Bird
+ 1. McHale
+ 1. Parish
+
+or even:
+
+ 3. Bird
+ 1. McHale
+ 8. Parish
+
+you'd get the exact same HTML output. The point is, if you want to,
+you can use ordinal numbers in your ordered Markdown lists, so that
+the numbers in your source match the numbers in your published HTML.
+But if you want to be lazy, you don't have to.
+
+If you do use lazy list numbering, however, you should still start the
+list with the number 1. At some point in the future, Markdown may support
+starting ordered lists at an arbitrary number.
+
+List markers typically start at the left margin, but may be indented by
+up to three spaces. List markers must be followed by one or more spaces
+or a tab.
+
+To make lists look nice, you can wrap items with hanging indents:
+
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+
+But if you want to be lazy, you don't have to:
+
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+
+If list items are separated by blank lines, Markdown will wrap the
+items in `<p>` tags in the HTML output. For example, this input:
+
+ * Bird
+ * Magic
+
+will turn into:
+
+ <ul>
+ <li>Bird</li>
+ <li>Magic</li>
+ </ul>
+
+But this:
+
+ * Bird
+
+ * Magic
+
+will turn into:
+
+ <ul>
+ <li><p>Bird</p></li>
+ <li><p>Magic</p></li>
+ </ul>
+
+List items may consist of multiple paragraphs. Each subsequent
+paragraph in a list item must be indented by either 4 spaces
+or one tab:
+
+ 1. This is a list item with two paragraphs. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit. Aliquam hendrerit
+ mi posuere lectus.
+
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet
+ vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
+ sit amet velit.
+
+ 2. Suspendisse id sem consectetuer libero luctus adipiscing.
+
+It looks nice if you indent every line of the subsequent
+paragraphs, but here again, Markdown will allow you to be
+lazy:
+
+ * This is a list item with two paragraphs.
+
+ This is the second paragraph in the list item. You're
+ only required to indent the first line. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit.
+
+ * Another item in the same list.
+
+To put a blockquote within a list item, the blockquote's `>`
+delimiters need to be indented:
+
+ * A list item with a blockquote:
+
+ > This is a blockquote
+ > inside a list item.
+
+To put a code block within a list item, the code block needs
+to be indented *twice* -- 8 spaces or two tabs:
+
+ * A list item with a code block:
+
+ <code goes here>
+
+
+It's worth noting that it's possible to trigger an ordered list by
+accident, by writing something like this:
+
+ 1986. What a great season.
+
+In other words, a *number-period-space* sequence at the beginning of a
+line. To avoid this, you can backslash-escape the period:
+
+ 1986\. What a great season.
+
+
+
+<h3 id="precode">Code Blocks</h3>
+
+Pre-formatted code blocks are used for writing about programming or
+markup source code. Rather than forming normal paragraphs, the lines
+of a code block are interpreted literally. Markdown wraps a code block
+in both `<pre>` and `<code>` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab. For example, given this input:
+
+ This is a normal paragraph:
+
+ This is a code block.
+
+Markdown will generate:
+
+ <p>This is a normal paragraph:</p>
+
+ <pre><code>This is a code block.
+ </code></pre>
+
+One level of indentation -- 4 spaces or 1 tab -- is removed from each
+line of the code block. For example, this:
+
+ Here is an example of AppleScript:
+
+ tell application "Foo"
+ beep
+ end tell
+
+will turn into:
+
+ <p>Here is an example of AppleScript:</p>
+
+ <pre><code>tell application "Foo"
+ beep
+ end tell
+ </code></pre>
+
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+
+ <div class="footer">
+ © 2004 Foo Corporation
+ </div>
+
+will turn into:
+
+ <pre><code><div class="footer">
+ &copy; 2004 Foo Corporation
+ </div>
+ </code></pre>
+
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+
+
+
+<h3 id="hr">Horizontal Rules</h3>
+
+You can produce a horizontal rule tag (`<hr />`) by placing three or
+more hyphens, asterisks, or underscores on a line by themselves. If you
+wish, you may use spaces between the hyphens or asterisks. Each of the
+following lines will produce a horizontal rule:
+
+ * * *
+
+ ***
+
+ *****
+
+ - - -
+
+ ---------------------------------------
+
+ _ _ _
+
+
+* * *
+
+<h2 id="span">Span Elements</h2>
+
+<h3 id="link">Links</h3>
+
+Markdown supports two style of links: *inline* and *reference*.
+
+In both styles, the link text is delimited by [square brackets].
+
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+
+ This is [an example](http://example.com/ "Title") inline link.
+
+ [This link](http://example.net/) has no title attribute.
+
+Will produce:
+
+ <p>This is <a href="http://example.com/" title="Title">
+ an example</a> inline link.</p>
+
+ <p><a href="http://example.net/">This link</a> has no
+ title attribute.</p>
+
+If you're referring to a local resource on the same server, you can
+use relative paths:
+
+ See my [About](/about/) page for details.
+
+Reference-style links use a second set of square brackets, inside
+which you place a label of your choosing to identify the link:
+
+ This is [an example][id] reference-style link.
+
+You can optionally use a space to separate the sets of brackets:
+
+ This is [an example] [id] reference-style link.
+
+Then, anywhere in the document, you define your link label like this,
+on a line by itself:
+
+ [id]: http://example.com/ "Optional Title Here"
+
+That is:
+
+* Square brackets containing the link identifier (optionally
+ indented from the left margin using up to three spaces);
+* followed by a colon;
+* followed by one or more spaces (or tabs);
+* followed by the URL for the link;
+* optionally followed by a title attribute for the link, enclosed
+ in double or single quotes.
+
+The link URL may, optionally, be surrounded by angle brackets:
+
+ [id]: <http://example.com/> "Optional Title Here"
+
+You can put the title attribute on the next line and use extra spaces
+or tabs for padding, which tends to look better with longer URLs:
+
+ [id]: http://example.com/longish/path/to/resource/here
+ "Optional Title Here"
+
+Link definitions are only used for creating links during Markdown
+processing, and are stripped from your document in the HTML output.
+
+Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are *not* case sensitive. E.g. these two links:
+
+ [link text][a]
+ [link text][A]
+
+are equivalent.
+
+The *implicit link name* shortcut allows you to omit the name of the
+link, in which case the link text itself is used as the name.
+Just use an empty set of square brackets -- e.g., to link the word
+"Google" to the google.com web site, you could simply write:
+
+ [Google][]
+
+And then define the link:
+
+ [Google]: http://google.com/
+
+Because link names may contain spaces, this shortcut even works for
+multiple words in the link text:
+
+ Visit [Daring Fireball][] for more information.
+
+And then define the link:
+
+ [Daring Fireball]: http://daringfireball.net/
+
+Link definitions can be placed anywhere in your Markdown document. I
+tend to put them immediately after each paragraph in which they're
+used, but if you want, you can put them all at the end of your
+document, sort of like footnotes.
+
+Here's an example of reference links in action:
+
+ I get 10 times more traffic from [Google] [1] than from
+ [Yahoo] [2] or [MSN] [3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+
+Using the implicit link name shortcut, you could instead write:
+
+ I get 10 times more traffic from [Google][] than from
+ [Yahoo][] or [MSN][].
+
+ [google]: http://google.com/ "Google"
+ [yahoo]: http://search.yahoo.com/ "Yahoo Search"
+ [msn]: http://search.msn.com/ "MSN Search"
+
+Both of the above examples will produce the following HTML output:
+
+ <p>I get 10 times more traffic from <a href="http://google.com/"
+ title="Google">Google</a> than from
+ <a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
+ or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
+
+For comparison, here is the same paragraph written using
+Markdown's inline link style:
+
+ I get 10 times more traffic from [Google](http://google.com/ "Google")
+ than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
+ [MSN](http://search.msn.com/ "MSN Search").
+
+The point of reference-style links is not that they're easier to
+write. The point is that with reference-style links, your document
+source is vastly more readable. Compare the above examples: using
+reference-style links, the paragraph itself is only 81 characters
+long; with inline-style links, it's 176 characters; and as raw HTML,
+it's 234 characters. In the raw HTML, there's more markup than there
+is text.
+
+With Markdown's reference-style links, a source document much more
+closely resembles the final output, as rendered in a browser. By
+allowing you to move the markup-related metadata out of the paragraph,
+you can add links without interrupting the narrative flow of your
+prose.
+
+
+<h3 id="em">Emphasis</h3>
+
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`<strong>` tag. E.g., this input:
+
+ *single asterisks*
+
+ _single underscores_
+
+ **double asterisks**
+
+ __double underscores__
+
+will produce:
+
+ <em>single asterisks</em>
+
+ <em>single underscores</em>
+
+ <strong>double asterisks</strong>
+
+ <strong>double underscores</strong>
+
+You can use whichever style you prefer; the lone restriction is that
+the same character must be used to open and close an emphasis span.
+
+Emphasis can be used in the middle of a word:
+
+ un*fucking*believable
+
+But if you surround an `*` or `_` with spaces, it'll be treated as a
+literal asterisk or underscore.
+
+To produce a literal asterisk or underscore at a position where it
+would otherwise be used as an emphasis delimiter, you can backslash
+escape it:
+
+ \*this text is surrounded by literal asterisks\*
+
+
+
+<h3 id="code">Code</h3>
+
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+
+ Use the `printf()` function.
+
+will produce:
+
+ <p>Use the <code>printf()</code> function.</p>
+
+To include a literal backtick character within a code span, you can use
+multiple backticks as the opening and closing delimiters:
+
+ ``There is a literal backtick (`) here.``
+
+which will produce this:
+
+ <p><code>There is a literal backtick (`) here.</code></p>
+
+The backtick delimiters surrounding a code span may include spaces --
+one after the opening, one before the closing. This allows you to place
+literal backtick characters at the beginning or end of a code span:
+
+ A single backtick in a code span: `` ` ``
+
+ A backtick-delimited string in a code span: `` `foo` ``
+
+will produce:
+
+ <p>A single backtick in a code span: <code>`</code></p>
+
+ <p>A backtick-delimited string in a code span: <code>`foo`</code></p>
+
+With a code span, ampersands and angle brackets are encoded as HTML
+entities automatically, which makes it easy to include example HTML
+tags. Markdown will turn this:
+
+ Please don't use any `<blink>` tags.
+
+into:
+
+ <p>Please don't use any <code><blink></code> tags.</p>
+
+You can write this:
+
+ `—` is the decimal-encoded equivalent of `—`.
+
+to produce:
+
+ <p><code>&#8212;</code> is the decimal-encoded
+ equivalent of <code>&mdash;</code>.</p>
+
+
+
+<h3 id="img">Images</h3>
+
+Admittedly, it's fairly difficult to devise a "natural" syntax for
+placing images into a plain text document format.
+
+Markdown uses an image syntax that is intended to resemble the syntax
+for links, allowing for two styles: *inline* and *reference*.
+
+Inline image syntax looks like this:
+
+ 
+
+ 
+
+That is:
+
+* An exclamation mark: `!`;
+* followed by a set of square brackets, containing the `alt`
+ attribute text for the image;
+* followed by a set of parentheses, containing the URL or path to
+ the image, and an optional `title` attribute enclosed in double
+ or single quotes.
+
+Reference-style image syntax looks like this:
+
+ ![Alt text][id]
+
+Where "id" is the name of a defined image reference. Image references
+are defined using syntax identical to link references:
+
+ [id]: url/to/image "Optional title attribute"
+
+As of this writing, Markdown has no syntax for specifying the
+dimensions of an image; if this is important to you, you can simply
+use regular HTML `<img>` tags.
+
+
+* * *
+
+
+<h2 id="misc">Miscellaneous</h2>
+
+<h3 id="autolink">Automatic Links</h3>
+
+Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:
+
+ <http://example.com/>
+
+Markdown will turn this into:
+
+ <a href="http://example.com/">http://example.com/</a>
+
+Automatic links for email addresses work similarly, except that
+Markdown will also perform a bit of randomized decimal and hex
+entity-encoding to help obscure your address from address-harvesting
+spambots. For example, Markdown will turn this:
+
+ <address@example.com>
+
+into something like this:
+
+ <a href="mailto:addre
+ ss@example.co
+ m">address@exa
+ mple.com</a>
+
+which will render in a browser as a clickable link to "address@example.com".
+
+(This sort of entity-encoding trick will indeed fool many, if not
+most, address-harvesting bots, but it definitely won't fool all of
+them. It's better than nothing, but an address published in this way
+will probably eventually start receiving spam.)
+
+
+
+<h3 id="backslash">Backslash Escapes</h3>
+
+Markdown allows you to use backslash escapes to generate literal
+characters which would otherwise have special meaning in Markdown's
+formatting syntax. For example, if you wanted to surround a word with
+literal asterisks (instead of an HTML `<em>` tag), you can backslashes
+before the asterisks, like this:
+
+ \*literal asterisks\*
+
+Markdown provides backslash escapes for the following characters:
+
+ \ backslash
+ ` backtick
+ * asterisk
+ _ underscore
+ {} curly braces
+ [] square brackets
+ () parentheses
+ # hash mark
+ + plus sign
+ - minus sign (hyphen)
+ . dot
+ ! exclamation mark
+
--- /dev/null
+> foo
+>
+> > bar
+>
+> foo
--- /dev/null
+## Unordered
+
+Asterisks tight:
+
+* asterisk 1
+* asterisk 2
+* asterisk 3
+
+
+Asterisks loose:
+
+* asterisk 1
+
+* asterisk 2
+
+* asterisk 3
+
+* * *
+
+Pluses tight:
+
++ Plus 1
++ Plus 2
++ Plus 3
+
+
+Pluses loose:
+
++ Plus 1
+
++ Plus 2
+
++ Plus 3
+
+* * *
+
+
+Minuses tight:
+
+- Minus 1
+- Minus 2
+- Minus 3
+
+
+Minuses loose:
+
+- Minus 1
+
+- Minus 2
+
+- Minus 3
+
+
+## Ordered
+
+Tight:
+
+1. First
+2. Second
+3. Third
+
+and:
+
+1. One
+2. Two
+3. Three
+
+
+Loose using tabs:
+
+1. First
+
+2. Second
+
+3. Third
+
+and using spaces:
+
+1. One
+
+2. Two
+
+3. Three
+
+Multiple paragraphs:
+
+1. Item 1, graf one.
+
+ Item 2. graf two. The quick brown fox jumped over the lazy dog's
+ back.
+
+2. Item 2.
+
+3. Item 3.
+
+
+
+## Nested
+
+* Tab
+ * Tab
+ * Tab
+
+Here's another:
+
+1. First
+2. Second:
+ * Fee
+ * Fie
+ * Foe
+3. Third
+
+Same thing but with paragraphs:
+
+1. First
+
+2. Second:
+ * Fee
+ * Fie
+ * Foe
+
+3. Third
--- /dev/null
+***This is strong and em.***
+
+So is ***this*** word.
+
+___This is strong and em.___
+
+So is ___this___ word.
--- /dev/null
++ this is a list item
+ indented with tabs
+
++ this is a list item
+ indented with spaces
+
+Code:
+
+ this code block is indented by one tab
+
+And:
+
+ this code block is indented by two tabs
+
+And:
+
+ + this is an example list item
+ indented with tabs
+
+ + this is an example list item
+ indented with spaces
--- /dev/null
+> A list within a blockquote:
+>
+> * asterisk 1
+> * asterisk 2
+> * asterisk 3
--- /dev/null
+
+Lorem ipsum {@id=lorem}
+=================================
+
+Dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor
+incididunt ut labore et dolore magna aliqua.
+
+* Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
+ nisi ut aliquip ex ea commodo consequat.{@class=first_item}
+
+* Duis aute irure dolor in reprehenderit in voluptate velit esse
+ cillum dolore eu fugiat nulla pariatur2. Excepteur sint occaecat
+ cupidatat non proident, sunt in culpa qui officia deserunt mollit
+ anim id est laborum.
+
+Duis aute **irure{@type=term}** dolor in reprehenderit in voluptate
+velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+occaecat cupidatat non proident, sunt in culpa qui officia deserunt
+mollit anim id est laborum.
--- /dev/null
+Lorem ipsum <yuri@domain.org>, etc.
+
+* An email address in a list
+* <yuri@domain.org>
+* Another item.
+
+
--- /dev/null
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua[^2]. Ut enim ad minim
+veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
+commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
+velit esse cillum dolore eu fugiat nulla pariatur[^1]. Excepteur sint
+occaecat cupidatat non proident, sunt in culpa qui officia deserunt
+mollit anim id est laborum.
+
+[^1]: Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit
+ aut fugit, sed quia consequuntur magni dolores eos qui ratione
+ voluptatem sequi nesciunt.
+
+ Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet,
+ consectetur, adipisci velit, sed quia non numquam eius modi
+ tempora incidunt ut labore et dolore magnam aliquam quaerat
+ voluptatem.
+
+[^2]: Sed ut perspiciatis unde omnis iste natus error sit voluptatem
+ accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
+ quae ab illo inventore veritatis et quasi architecto beatae vitae
+ dicta sunt explicabo.
+
+* Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
+ eiusmod tempor incididunt ut labore et dolore[^foo] magna aliqua.
+
+[^foo]: Ut enim ad minim veniam, quis nostrud exercitation ullamco
+ laboris nisi ut aliquip ex ea commodo consequat.
+
+Duis aute irure dolor in reprehenderit in voluptate
+velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+occaecat cupidatat non proident, sunt in culpa qui officia deserunt
+mollit anim id est laborum.
--- /dev/null
+## A plain header
+
+Let's first have a plain header
+
+An underlined header
+====================
+
+(That's also useful)
+
+# A header with a [link](http://www.link.com)
+
+First with a hash
+
+Another with a [link][a]
+------------------------
+This time underlined
+
+[a]: http://www.link.com/
--- /dev/null
+AT&T has an ampersand in their name.
+
+AT&T is another way to write it.
+
+This & that.
+
+4 < 5.
+
+6 > 5.
+
+Here's a [link] [1] with an ampersand in the URL.
+
+Here's a link with an amersand in the link text: [AT&T] [2].
+
+Here's an inline [link](/script?foo=1&bar=2).
+
+Here's an inline [link](</script?foo=1&bar=2>).
+
+
+[1]: http://example.com/?foo=1&bar=2
+[2]: http://att.com/ "AT&T"
\ No newline at end of file
--- /dev/null
+Link: <http://example.com/>.
+
+With an ampersand: <http://example.com/?foo=1&bar=2>
+
+* In a list?
+* <http://example.com/>
+* It should.
+
+> Blockquoted: <http://example.com/>
+
+Auto-links should not occur here: `<http://example.com/>`
+
+ or here: <http://example.com/>
\ No newline at end of file
--- /dev/null
+These should all get escaped:
+
+Backslash: \\
+
+Backtick: \`
+
+Asterisk: \*
+
+Underscore: \_
+
+Left brace: \{
+
+Right brace: \}
+
+Left bracket: \[
+
+Right bracket: \]
+
+Left paren: \(
+
+Right paren: \)
+
+Greater-than: \>
+
+Hash: \#
+
+Period: \.
+
+Bang: \!
+
+Plus: \+
+
+Minus: \-
+
+
+
+These should not, because they occur within a code block:
+
+ Backslash: \\
+
+ Backtick: \`
+
+ Asterisk: \*
+
+ Underscore: \_
+
+ Left brace: \{
+
+ Right brace: \}
+
+ Left bracket: \[
+
+ Right bracket: \]
+
+ Left paren: \(
+
+ Right paren: \)
+
+ Greater-than: \>
+
+ Hash: \#
+
+ Period: \.
+
+ Bang: \!
+
+ Plus: \+
+
+ Minus: \-
+
+
+Nor should these, which occur in code spans:
+
+Backslash: `\\`
+
+Backtick: `` \` ``
+
+Asterisk: `\*`
+
+Underscore: `\_`
+
+Left brace: `\{`
+
+Right brace: `\}`
+
+Left bracket: `\[`
+
+Right bracket: `\]`
+
+Left paren: `\(`
+
+Right paren: `\)`
+
+Greater-than: `\>`
+
+Hash: `\#`
+
+Period: `\.`
+
+Bang: `\!`
+
+Plus: `\+`
+
+Minus: `\-`
+
+
+These should get escaped, even though they're matching pairs for
+other Markdown constructs:
+
+\*asterisks\*
+
+\_underscores\_
+
+\`backticks\`
+
+This is a code span with a literal backslash-backtick sequence: `` \` ``
+
+This is a tag with unescaped backticks <span attr='`ticks`'>bar</span>.
+
+This is a tag with backslashes <span attr='\\backslashes\\'>bar</span>.
--- /dev/null
+> Example:
+>
+> sub status {
+> print "working";
+> }
+>
+> Or:
+>
+> sub status {
+> return "working";
+> }
--- /dev/null
+ code block on the first line
+
+Regular text.
+
+ code block indented by spaces
+
+Regular text.
+
+ the lines in this block
+ all contain trailing spaces
+
+Regular Text.
+
+ code block on the last line
\ No newline at end of file
--- /dev/null
+`<test a="` content of attribute `">`
+
+Fix for backticks within HTML tag: <span attr='`ticks`'>like this</span>
+
+Here's how you put `` `backticks` `` in a code span.
\ No newline at end of file
--- /dev/null
+In Markdown 1.0.0 and earlier. Version
+8. This line turns into a list item.
+Because a hard-wrapped line in the
+middle of a paragraph looked like a
+list item.
+
+Here's one with a bullet.
+* criminey.
--- /dev/null
+Dashes:
+
+---
+
+ ---
+
+ ---
+
+ ---
+
+ ---
+
+- - -
+
+ - - -
+
+ - - -
+
+ - - -
+
+ - - -
+
+
+Asterisks:
+
+***
+
+ ***
+
+ ***
+
+ ***
+
+ ***
+
+* * *
+
+ * * *
+
+ * * *
+
+ * * *
+
+ * * *
+
+
+Underscores:
+
+___
+
+ ___
+
+ ___
+
+ ___
+
+ ___
+
+_ _ _
+
+ _ _ _
+
+ _ _ _
+
+ _ _ _
+
+ _ _ _
--- /dev/null
+
+
+
+
+Inline within a paragraph: [alt text](/url/).
+
+
+
+
+
+
+
+.
+
+![Empty]()
+
+.jpg)
+
+
+![alt text][foo]
+
+ [foo]: /url/
+
+![alt text][bar]
+
+ [bar]: /url/ "Title here"
\ No newline at end of file
--- /dev/null
+Simple block on one line:
+
+<div>foo</div>
+
+And nested without indentation:
+
+<div>
+<div>
+<div>
+foo
+</div>
+<div style=">"/>
+</div>
+<div>bar</div>
+</div>
+
+And with attributes:
+
+<div>
+ <div id="foo">
+ </div>
+</div>
+
+This was broken in 1.0.2b7:
+
+<div class="inlinepage">
+<div class="toggleableend">
+foo
+</div>
+</div>
--- /dev/null
+Here's a simple block:
+
+<div>
+ foo
+</div>
+
+This should be a code block, though:
+
+ <div>
+ foo
+ </div>
+
+As should this:
+
+ <div>foo</div>
+
+Now, nested:
+
+<div>
+ <div>
+ <div>
+ foo
+ </div>
+ </div>
+</div>
+
+This should just be an HTML comment:
+
+<!-- Comment -->
+
+Multiline:
+
+<!--
+Blah
+Blah
+-->
+
+Code block:
+
+ <!-- Comment -->
+
+Just plain comment, with trailing spaces on the line:
+
+<!-- foo -->
+
+Code:
+
+ <hr />
+
+Hr's:
+
+<hr>
+
+<hr/>
+
+<hr />
+
+<hr>
+
+<hr/>
+
+<hr />
+
+<hr class="foo" id="bar" />
+
+<hr class="foo" id="bar"/>
+
+<hr class="foo" id="bar" >
+
--- /dev/null
+Paragraph one.
+
+<!-- This is a simple comment -->
+
+<!--
+ This is another comment.
+-->
+
+Paragraph two.
+
+<!-- one comment block -- -- with two comments -->
+
+The end.
--- /dev/null
+Just a [URL](/url/).
+
+[URL and title](/url/ "title").
+
+[URL and title](/url/ "title preceded by two spaces").
+
+[URL and title](/url/ "title preceded by a tab").
+
+[URL and title](/url/ "title has spaces afterward" ).
+
+[URL wrapped in angle brackets](</url/>).
+
+[URL w/ angle brackets + title](</url/> "Here's the title").
+
+[Empty]().
+
+[With parens in the URL](http://en.wikipedia.org/wiki/WIMP_(computing))
+
+(With outer parens and [parens in url](/foo(bar)))
+
+
+[With parens in the URL](/foo(bar) "and a title")
+
+(With outer parens and [parens in url](/foo(bar) "and a title"))
--- /dev/null
+Foo [bar] [1].
+
+Foo [bar][1].
+
+Foo [bar]
+[1].
+
+[1]: /url/ "Title"
+
+
+With [embedded [brackets]] [b].
+
+
+Indented [once][].
+
+Indented [twice][].
+
+Indented [thrice][].
+
+Indented [four][] times.
+
+ [once]: /url
+
+ [twice]: /url
+
+ [thrice]: /url
+
+ [four]: /url
+
+
+[b]: /url/
+
+* * *
+
+[this] [this] should work
+
+So should [this][this].
+
+And [this] [].
+
+And [this][].
+
+And [this].
+
+But not [that] [].
+
+Nor [that][].
+
+Nor [that].
+
+[Something in brackets like [this][] should work]
+
+[Same with [this].]
+
+In this case, [this](/somethingelse/) points to something else.
+
+Backslashing should suppress \[this] and [this\].
+
+[this]: foo
+
+
+* * *
+
+Here's one where the [link
+breaks] across lines.
+
+Here's another where the [link
+breaks] across lines, but with a line-ending space.
+
+
+[link breaks]: /url/
--- /dev/null
+This is the [simple case].
+
+[simple case]: /simple
+
+
+
+This one has a [line
+break].
+
+This one has a [line
+break] with a line-ending space.
+
+[line break]: /foo
+
+
+[this] [that] and the [other]
+
+[this]: /this
+[that]: /that
+[other]: /other
--- /dev/null
+Foo [bar][].
+
+Foo [bar](/url/ "Title with "quotes" inside").
+
+
+ [bar]: /url/ "Title with "quotes" inside"
+
--- /dev/null
+Markdown: Basics
+================
+
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a class="selected" title="Markdown Basics">Basics</a></li>
+ <li><a href="/projects/markdown/syntax" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+
+
+Getting the Gist of Markdown's Formatting Syntax
+------------------------------------------------
+
+This page offers a brief overview of what it's like to use Markdown.
+The [syntax page] [s] provides complete, detailed documentation for
+every feature, but Markdown should be very easy to pick up simply by
+looking at a few examples of it in action. The examples on this page
+are written in a before/after style, showing example syntax and the
+HTML output produced by Markdown.
+
+It's also helpful to simply try Markdown out; the [Dingus] [d] is a
+web application that allows you type your own Markdown-formatted text
+and translate it to XHTML.
+
+**Note:** This document is itself written using Markdown; you
+can [see the source for it by adding '.text' to the URL] [src].
+
+ [s]: /projects/markdown/syntax "Markdown Syntax"
+ [d]: /projects/markdown/dingus "Markdown Dingus"
+ [src]: /projects/markdown/basics.text
+
+
+## Paragraphs, Headers, Blockquotes ##
+
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing spaces or tabs is considered
+blank.) Normal paragraphs should not be indented with spaces or tabs.
+
+Markdown offers two styles of headers: *Setext* and *atx*.
+Setext-style headers for `<h1>` and `<h2>` are created by
+"underlining" with equal signs (`=`) and hyphens (`-`), respectively.
+To create an atx-style header, you put 1-6 hash marks (`#`) at the
+beginning of the line -- the number of hashes equals the resulting
+HTML header level.
+
+Blockquotes are indicated using email-style '`>`' angle brackets.
+
+Markdown:
+
+ A First Level Header
+ ====================
+
+ A Second Level Header
+ ---------------------
+
+ Now is the time for all good men to come to
+ the aid of their country. This is just a
+ regular paragraph.
+
+ The quick brown fox jumped over the lazy
+ dog's back.
+
+ ### Header 3
+
+ > This is a blockquote.
+ >
+ > This is the second paragraph in the blockquote.
+ >
+ > ## This is an H2 in a blockquote
+
+
+Output:
+
+ <h1>A First Level Header</h1>
+
+ <h2>A Second Level Header</h2>
+
+ <p>Now is the time for all good men to come to
+ the aid of their country. This is just a
+ regular paragraph.</p>
+
+ <p>The quick brown fox jumped over the lazy
+ dog's back.</p>
+
+ <h3>Header 3</h3>
+
+ <blockquote>
+ <p>This is a blockquote.</p>
+
+ <p>This is the second paragraph in the blockquote.</p>
+
+ <h2>This is an H2 in a blockquote</h2>
+ </blockquote>
+
+
+
+### Phrase Emphasis ###
+
+Markdown uses asterisks and underscores to indicate spans of emphasis.
+
+Markdown:
+
+ Some of these words *are emphasized*.
+ Some of these words _are emphasized also_.
+
+ Use two asterisks for **strong emphasis**.
+ Or, if you prefer, __use two underscores instead__.
+
+Output:
+
+ <p>Some of these words <em>are emphasized</em>.
+ Some of these words <em>are emphasized also</em>.</p>
+
+ <p>Use two asterisks for <strong>strong emphasis</strong>.
+ Or, if you prefer, <strong>use two underscores instead</strong>.</p>
+
+
+
+## Lists ##
+
+Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
+`+`, and `-`) as list markers. These three markers are
+interchangable; this:
+
+ * Candy.
+ * Gum.
+ * Booze.
+
+this:
+
+ + Candy.
+ + Gum.
+ + Booze.
+
+and this:
+
+ - Candy.
+ - Gum.
+ - Booze.
+
+all produce the same output:
+
+ <ul>
+ <li>Candy.</li>
+ <li>Gum.</li>
+ <li>Booze.</li>
+ </ul>
+
+Ordered (numbered) lists use regular numbers, followed by periods, as
+list markers:
+
+ 1. Red
+ 2. Green
+ 3. Blue
+
+Output:
+
+ <ol>
+ <li>Red</li>
+ <li>Green</li>
+ <li>Blue</li>
+ </ol>
+
+If you put blank lines between items, you'll get `<p>` tags for the
+list item text. You can create multi-paragraph list items by indenting
+the paragraphs by 4 spaces or 1 tab:
+
+ * A list item.
+
+ With multiple paragraphs.
+
+ * Another item in the list.
+
+Output:
+
+ <ul>
+ <li><p>A list item.</p>
+ <p>With multiple paragraphs.</p></li>
+ <li><p>Another item in the list.</p></li>
+ </ul>
+
+
+
+### Links ###
+
+Markdown supports two styles for creating links: *inline* and
+*reference*. With both styles, you use square brackets to delimit the
+text you want to turn into a link.
+
+Inline-style links use parentheses immediately after the link text.
+For example:
+
+ This is an [example link](http://example.com/).
+
+Output:
+
+ <p>This is an <a href="http://example.com/">
+ example link</a>.</p>
+
+Optionally, you may include a title attribute in the parentheses:
+
+ This is an [example link](http://example.com/ "With a Title").
+
+Output:
+
+ <p>This is an <a href="http://example.com/" title="With a Title">
+ example link</a>.</p>
+
+Reference-style links allow you to refer to your links by names, which
+you define elsewhere in your document:
+
+ I get 10 times more traffic from [Google][1] than from
+ [Yahoo][2] or [MSN][3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+
+Output:
+
+ <p>I get 10 times more traffic from <a href="http://google.com/"
+ title="Google">Google</a> than from <a href="http://search.yahoo.com/"
+ title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/"
+ title="MSN Search">MSN</a>.</p>
+
+The title attribute is optional. Link names may contain letters,
+numbers and spaces, but are *not* case sensitive:
+
+ I start my morning with a cup of coffee and
+ [The New York Times][NY Times].
+
+ [ny times]: http://www.nytimes.com/
+
+Output:
+
+ <p>I start my morning with a cup of coffee and
+ <a href="http://www.nytimes.com/">The New York Times</a>.</p>
+
+
+### Images ###
+
+Image syntax is very much like link syntax.
+
+Inline (titles are optional):
+
+ 
+
+Reference-style:
+
+ ![alt text][id]
+
+ [id]: /path/to/img.jpg "Title"
+
+Both of the above examples produce the same output:
+
+ <img src="/path/to/img.jpg" alt="alt text" title="Title" />
+
+
+
+### Code ###
+
+In a regular paragraph, you can create code span by wrapping text in
+backtick quotes. Any ampersands (`&`) and angle brackets (`<` or
+`>`) will automatically be translated into HTML entities. This makes
+it easy to use Markdown to write about HTML example code:
+
+ I strongly recommend against using any `<blink>` tags.
+
+ I wish SmartyPants used named entities like `—`
+ instead of decimal-encoded entites like `—`.
+
+Output:
+
+ <p>I strongly recommend against using any
+ <code><blink></code> tags.</p>
+
+ <p>I wish SmartyPants used named entities like
+ <code>&mdash;</code> instead of decimal-encoded
+ entites like <code>&#8212;</code>.</p>
+
+
+To specify an entire block of pre-formatted code, indent every line of
+the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`,
+and `>` characters will be escaped automatically.
+
+Markdown:
+
+ If you want your page to validate under XHTML 1.0 Strict,
+ you've got to put paragraph tags in your blockquotes:
+
+ <blockquote>
+ <p>For example.</p>
+ </blockquote>
+
+Output:
+
+ <p>If you want your page to validate under XHTML 1.0 Strict,
+ you've got to put paragraph tags in your blockquotes:</p>
+
+ <pre><code><blockquote>
+ <p>For example.</p>
+ </blockquote>
+ </code></pre>
--- /dev/null
+Markdown: Syntax
+================
+
+<ul id="ProjectSubmenu">
+ <li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li>
+ <li><a href="/projects/markdown/basics" title="Markdown Basics">Basics</a></li>
+ <li><a class="selected" title="Markdown Syntax Documentation">Syntax</a></li>
+ <li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li>
+ <li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li>
+</ul>
+
+
+* [Overview](#overview)
+ * [Philosophy](#philosophy)
+ * [Inline HTML](#html)
+ * [Automatic Escaping for Special Characters](#autoescape)
+* [Block Elements](#block)
+ * [Paragraphs and Line Breaks](#p)
+ * [Headers](#header)
+ * [Blockquotes](#blockquote)
+ * [Lists](#list)
+ * [Code Blocks](#precode)
+ * [Horizontal Rules](#hr)
+* [Span Elements](#span)
+ * [Links](#link)
+ * [Emphasis](#em)
+ * [Code](#code)
+ * [Images](#img)
+* [Miscellaneous](#misc)
+ * [Backslash Escapes](#backslash)
+ * [Automatic Links](#autolink)
+
+
+**Note:** This document is itself written using Markdown; you
+can [see the source for it by adding '.text' to the URL][src].
+
+ [src]: /projects/markdown/syntax.text
+
+* * *
+
+<h2 id="overview">Overview</h2>
+
+<h3 id="philosophy">Philosophy</h3>
+
+Markdown is intended to be as easy-to-read and easy-to-write as is feasible.
+
+Readability, however, is emphasized above all else. A Markdown-formatted
+document should be publishable as-is, as plain text, without looking
+like it's been marked up with tags or formatting instructions. While
+Markdown's syntax has been influenced by several existing text-to-HTML
+filters -- including [Setext] [1], [atx] [2], [Textile] [3], [reStructuredText] [4],
+[Grutatext] [5], and [EtText] [6] -- the single biggest source of
+inspiration for Markdown's syntax is the format of plain text email.
+
+ [1]: http://docutils.sourceforge.net/mirror/setext.html
+ [2]: http://www.aaronsw.com/2002/atx/
+ [3]: http://textism.com/tools/textile/
+ [4]: http://docutils.sourceforge.net/rst.html
+ [5]: http://www.triptico.com/software/grutatxt.html
+ [6]: http://ettext.taint.org/doc/
+
+To this end, Markdown's syntax is comprised entirely of punctuation
+characters, which punctuation characters have been carefully chosen so
+as to look like what they mean. E.g., asterisks around a word actually
+look like \*emphasis\*. Markdown lists look like, well, lists. Even
+blockquotes look like quoted passages of text, assuming you've ever
+used email.
+
+
+
+<h3 id="html">Inline HTML</h3>
+
+Markdown's syntax is intended for one purpose: to be used as a
+format for *writing* for the web.
+
+Markdown is not a replacement for HTML, or even close to it. Its
+syntax is very small, corresponding only to a very small subset of
+HTML tags. The idea is *not* to create a syntax that makes it easier
+to insert HTML tags. In my opinion, HTML tags are already easy to
+insert. The idea for Markdown is to make it easy to read, write, and
+edit prose. HTML is a *publishing* format; Markdown is a *writing*
+format. Thus, Markdown's formatting syntax only addresses issues that
+can be conveyed in plain text.
+
+For any markup that is not covered by Markdown's syntax, you simply
+use HTML itself. There's no need to preface it or delimit it to
+indicate that you're switching from Markdown to HTML; you just use
+the tags.
+
+The only restrictions are that block-level HTML elements -- e.g. `<div>`,
+`<table>`, `<pre>`, `<p>`, etc. -- must be separated from surrounding
+content by blank lines, and the start and end tags of the block should
+not be indented with tabs or spaces. Markdown is smart enough not
+to add extra (unwanted) `<p>` tags around HTML block-level tags.
+
+For example, to add an HTML table to a Markdown article:
+
+ This is a regular paragraph.
+
+ <table>
+ <tr>
+ <td>Foo</td>
+ </tr>
+ </table>
+
+ This is another regular paragraph.
+
+Note that Markdown formatting syntax is not processed within block-level
+HTML tags. E.g., you can't use Markdown-style `*emphasis*` inside an
+HTML block.
+
+Span-level HTML tags -- e.g. `<span>`, `<cite>`, or `<del>` -- can be
+used anywhere in a Markdown paragraph, list item, or header. If you
+want, you can even use HTML tags instead of Markdown formatting; e.g. if
+you'd prefer to use HTML `<a>` or `<img>` tags instead of Markdown's
+link or image syntax, go right ahead.
+
+Unlike block-level HTML tags, Markdown syntax *is* processed within
+span-level tags.
+
+
+<h3 id="autoescape">Automatic Escaping for Special Characters</h3>
+
+In HTML, there are two characters that demand special treatment: `<`
+and `&`. Left angle brackets are used to start tags; ampersands are
+used to denote HTML entities. If you want to use them as literal
+characters, you must escape them as entities, e.g. `<`, and
+`&`.
+
+Ampersands in particular are bedeviling for web writers. If you want to
+write about 'AT&T', you need to write '`AT&T`'. You even need to
+escape ampersands within URLs. Thus, if you want to link to:
+
+ http://images.google.com/images?num=30&q=larry+bird
+
+you need to encode the URL as:
+
+ http://images.google.com/images?num=30&q=larry+bird
+
+in your anchor tag `href` attribute. Needless to say, this is easy to
+forget, and is probably the single most common source of HTML validation
+errors in otherwise well-marked-up web sites.
+
+Markdown allows you to use these characters naturally, taking care of
+all the necessary escaping for you. If you use an ampersand as part of
+an HTML entity, it remains unchanged; otherwise it will be translated
+into `&`.
+
+So, if you want to include a copyright symbol in your article, you can write:
+
+ ©
+
+and Markdown will leave it alone. But if you write:
+
+ AT&T
+
+Markdown will translate it to:
+
+ AT&T
+
+Similarly, because Markdown supports [inline HTML](#html), if you use
+angle brackets as delimiters for HTML tags, Markdown will treat them as
+such. But if you write:
+
+ 4 < 5
+
+Markdown will translate it to:
+
+ 4 < 5
+
+However, inside Markdown code spans and blocks, angle brackets and
+ampersands are *always* encoded automatically. This makes it easy to use
+Markdown to write about HTML code. (As opposed to raw HTML, which is a
+terrible format for writing about HTML syntax, because every single `<`
+and `&` in your example code needs to be escaped.)
+
+
+* * *
+
+
+<h2 id="block">Block Elements</h2>
+
+
+<h3 id="p">Paragraphs and Line Breaks</h3>
+
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like a
+blank line -- a line containing nothing but spaces or tabs is considered
+blank.) Normal paragraphs should not be indented with spaces or tabs.
+
+The implication of the "one or more consecutive lines of text" rule is
+that Markdown supports "hard-wrapped" text paragraphs. This differs
+significantly from most other text-to-HTML formatters (including Movable
+Type's "Convert Line Breaks" option) which translate every line break
+character in a paragraph into a `<br />` tag.
+
+When you *do* want to insert a `<br />` break tag using Markdown, you
+end a line with two or more spaces, then type return.
+
+Yes, this takes a tad more effort to create a `<br />`, but a simplistic
+"every line break is a `<br />`" rule wouldn't work for Markdown.
+Markdown's email-style [blockquoting][bq] and multi-paragraph [list items][l]
+work best -- and look better -- when you format them with hard breaks.
+
+ [bq]: #blockquote
+ [l]: #list
+
+
+
+<h3 id="header">Headers</h3>
+
+Markdown supports two styles of headers, [Setext] [1] and [atx] [2].
+
+Setext-style headers are "underlined" using equal signs (for first-level
+headers) and dashes (for second-level headers). For example:
+
+ This is an H1
+ =============
+
+ This is an H2
+ -------------
+
+Any number of underlining `=`'s or `-`'s will work.
+
+Atx-style headers use 1-6 hash characters at the start of the line,
+corresponding to header levels 1-6. For example:
+
+ # This is an H1
+
+ ## This is an H2
+
+ ###### This is an H6
+
+Optionally, you may "close" atx-style headers. This is purely
+cosmetic -- you can use this if you think it looks better. The
+closing hashes don't even need to match the number of hashes
+used to open the header. (The number of opening hashes
+determines the header level.) :
+
+ # This is an H1 #
+
+ ## This is an H2 ##
+
+ ### This is an H3 ######
+
+
+<h3 id="blockquote">Blockquotes</h3>
+
+Markdown uses email-style `>` characters for blockquoting. If you're
+familiar with quoting passages of text in an email message, then you
+know how to create a blockquote in Markdown. It looks best if you hard
+wrap the text and put a `>` before every line:
+
+ > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ > consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ > Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+ >
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ > id sem consectetuer libero luctus adipiscing.
+
+Markdown allows you to be lazy and only put the `>` before the first
+line of a hard-wrapped paragraph:
+
+ > This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
+ consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
+
+ > Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
+ id sem consectetuer libero luctus adipiscing.
+
+Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by
+adding additional levels of `>`:
+
+ > This is the first level of quoting.
+ >
+ > > This is nested blockquote.
+ >
+ > Back to the first level.
+
+Blockquotes can contain other Markdown elements, including headers, lists,
+and code blocks:
+
+ > ## This is a header.
+ >
+ > 1. This is the first list item.
+ > 2. This is the second list item.
+ >
+ > Here's some example code:
+ >
+ > return shell_exec("echo $input | $markdown_script");
+
+Any decent text editor should make email-style quoting easy. For
+example, with BBEdit, you can make a selection and choose Increase
+Quote Level from the Text menu.
+
+
+<h3 id="list">Lists</h3>
+
+Markdown supports ordered (numbered) and unordered (bulleted) lists.
+
+Unordered lists use asterisks, pluses, and hyphens -- interchangably
+-- as list markers:
+
+ * Red
+ * Green
+ * Blue
+
+is equivalent to:
+
+ + Red
+ + Green
+ + Blue
+
+and:
+
+ - Red
+ - Green
+ - Blue
+
+Ordered lists use numbers followed by periods:
+
+ 1. Bird
+ 2. McHale
+ 3. Parish
+
+It's important to note that the actual numbers you use to mark the
+list have no effect on the HTML output Markdown produces. The HTML
+Markdown produces from the above list is:
+
+ <ol>
+ <li>Bird</li>
+ <li>McHale</li>
+ <li>Parish</li>
+ </ol>
+
+If you instead wrote the list in Markdown like this:
+
+ 1. Bird
+ 1. McHale
+ 1. Parish
+
+or even:
+
+ 3. Bird
+ 1. McHale
+ 8. Parish
+
+you'd get the exact same HTML output. The point is, if you want to,
+you can use ordinal numbers in your ordered Markdown lists, so that
+the numbers in your source match the numbers in your published HTML.
+But if you want to be lazy, you don't have to.
+
+If you do use lazy list numbering, however, you should still start the
+list with the number 1. At some point in the future, Markdown may support
+starting ordered lists at an arbitrary number.
+
+List markers typically start at the left margin, but may be indented by
+up to three spaces. List markers must be followed by one or more spaces
+or a tab.
+
+To make lists look nice, you can wrap items with hanging indents:
+
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+
+But if you want to be lazy, you don't have to:
+
+ * Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
+ Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi,
+ viverra nec, fringilla in, laoreet vitae, risus.
+ * Donec sit amet nisl. Aliquam semper ipsum sit amet velit.
+ Suspendisse id sem consectetuer libero luctus adipiscing.
+
+If list items are separated by blank lines, Markdown will wrap the
+items in `<p>` tags in the HTML output. For example, this input:
+
+ * Bird
+ * Magic
+
+will turn into:
+
+ <ul>
+ <li>Bird</li>
+ <li>Magic</li>
+ </ul>
+
+But this:
+
+ * Bird
+
+ * Magic
+
+will turn into:
+
+ <ul>
+ <li><p>Bird</p></li>
+ <li><p>Magic</p></li>
+ </ul>
+
+List items may consist of multiple paragraphs. Each subsequent
+paragraph in a list item must be indented by either 4 spaces
+or one tab:
+
+ 1. This is a list item with two paragraphs. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit. Aliquam hendrerit
+ mi posuere lectus.
+
+ Vestibulum enim wisi, viverra nec, fringilla in, laoreet
+ vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
+ sit amet velit.
+
+ 2. Suspendisse id sem consectetuer libero luctus adipiscing.
+
+It looks nice if you indent every line of the subsequent
+paragraphs, but here again, Markdown will allow you to be
+lazy:
+
+ * This is a list item with two paragraphs.
+
+ This is the second paragraph in the list item. You're
+ only required to indent the first line. Lorem ipsum dolor
+ sit amet, consectetuer adipiscing elit.
+
+ * Another item in the same list.
+
+To put a blockquote within a list item, the blockquote's `>`
+delimiters need to be indented:
+
+ * A list item with a blockquote:
+
+ > This is a blockquote
+ > inside a list item.
+
+To put a code block within a list item, the code block needs
+to be indented *twice* -- 8 spaces or two tabs:
+
+ * A list item with a code block:
+
+ <code goes here>
+
+
+It's worth noting that it's possible to trigger an ordered list by
+accident, by writing something like this:
+
+ 1986. What a great season.
+
+In other words, a *number-period-space* sequence at the beginning of a
+line. To avoid this, you can backslash-escape the period:
+
+ 1986\. What a great season.
+
+
+
+<h3 id="precode">Code Blocks</h3>
+
+Pre-formatted code blocks are used for writing about programming or
+markup source code. Rather than forming normal paragraphs, the lines
+of a code block are interpreted literally. Markdown wraps a code block
+in both `<pre>` and `<code>` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab. For example, given this input:
+
+ This is a normal paragraph:
+
+ This is a code block.
+
+Markdown will generate:
+
+ <p>This is a normal paragraph:</p>
+
+ <pre><code>This is a code block.
+ </code></pre>
+
+One level of indentation -- 4 spaces or 1 tab -- is removed from each
+line of the code block. For example, this:
+
+ Here is an example of AppleScript:
+
+ tell application "Foo"
+ beep
+ end tell
+
+will turn into:
+
+ <p>Here is an example of AppleScript:</p>
+
+ <pre><code>tell application "Foo"
+ beep
+ end tell
+ </code></pre>
+
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+
+ <div class="footer">
+ © 2004 Foo Corporation
+ </div>
+
+will turn into:
+
+ <pre><code><div class="footer">
+ &copy; 2004 Foo Corporation
+ </div>
+ </code></pre>
+
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+
+
+
+<h3 id="hr">Horizontal Rules</h3>
+
+You can produce a horizontal rule tag (`<hr />`) by placing three or
+more hyphens, asterisks, or underscores on a line by themselves. If you
+wish, you may use spaces between the hyphens or asterisks. Each of the
+following lines will produce a horizontal rule:
+
+ * * *
+
+ ***
+
+ *****
+
+ - - -
+
+ ---------------------------------------
+
+ _ _ _
+
+
+* * *
+
+<h2 id="span">Span Elements</h2>
+
+<h3 id="link">Links</h3>
+
+Markdown supports two style of links: *inline* and *reference*.
+
+In both styles, the link text is delimited by [square brackets].
+
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+
+ This is [an example](http://example.com/ "Title") inline link.
+
+ [This link](http://example.net/) has no title attribute.
+
+Will produce:
+
+ <p>This is <a href="http://example.com/" title="Title">
+ an example</a> inline link.</p>
+
+ <p><a href="http://example.net/">This link</a> has no
+ title attribute.</p>
+
+If you're referring to a local resource on the same server, you can
+use relative paths:
+
+ See my [About](/about/) page for details.
+
+Reference-style links use a second set of square brackets, inside
+which you place a label of your choosing to identify the link:
+
+ This is [an example][id] reference-style link.
+
+You can optionally use a space to separate the sets of brackets:
+
+ This is [an example] [id] reference-style link.
+
+Then, anywhere in the document, you define your link label like this,
+on a line by itself:
+
+ [id]: http://example.com/ "Optional Title Here"
+
+That is:
+
+* Square brackets containing the link identifier (optionally
+ indented from the left margin using up to three spaces);
+* followed by a colon;
+* followed by one or more spaces (or tabs);
+* followed by the URL for the link;
+* optionally followed by a title attribute for the link, enclosed
+ in double or single quotes.
+
+The link URL may, optionally, be surrounded by angle brackets:
+
+ [id]: <http://example.com/> "Optional Title Here"
+
+You can put the title attribute on the next line and use extra spaces
+or tabs for padding, which tends to look better with longer URLs:
+
+ [id]: http://example.com/longish/path/to/resource/here
+ "Optional Title Here"
+
+Link definitions are only used for creating links during Markdown
+processing, and are stripped from your document in the HTML output.
+
+Link definition names may constist of letters, numbers, spaces, and punctuation -- but they are *not* case sensitive. E.g. these two links:
+
+ [link text][a]
+ [link text][A]
+
+are equivalent.
+
+The *implicit link name* shortcut allows you to omit the name of the
+link, in which case the link text itself is used as the name.
+Just use an empty set of square brackets -- e.g., to link the word
+"Google" to the google.com web site, you could simply write:
+
+ [Google][]
+
+And then define the link:
+
+ [Google]: http://google.com/
+
+Because link names may contain spaces, this shortcut even works for
+multiple words in the link text:
+
+ Visit [Daring Fireball][] for more information.
+
+And then define the link:
+
+ [Daring Fireball]: http://daringfireball.net/
+
+Link definitions can be placed anywhere in your Markdown document. I
+tend to put them immediately after each paragraph in which they're
+used, but if you want, you can put them all at the end of your
+document, sort of like footnotes.
+
+Here's an example of reference links in action:
+
+ I get 10 times more traffic from [Google] [1] than from
+ [Yahoo] [2] or [MSN] [3].
+
+ [1]: http://google.com/ "Google"
+ [2]: http://search.yahoo.com/ "Yahoo Search"
+ [3]: http://search.msn.com/ "MSN Search"
+
+Using the implicit link name shortcut, you could instead write:
+
+ I get 10 times more traffic from [Google][] than from
+ [Yahoo][] or [MSN][].
+
+ [google]: http://google.com/ "Google"
+ [yahoo]: http://search.yahoo.com/ "Yahoo Search"
+ [msn]: http://search.msn.com/ "MSN Search"
+
+Both of the above examples will produce the following HTML output:
+
+ <p>I get 10 times more traffic from <a href="http://google.com/"
+ title="Google">Google</a> than from
+ <a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a>
+ or <a href="http://search.msn.com/" title="MSN Search">MSN</a>.</p>
+
+For comparison, here is the same paragraph written using
+Markdown's inline link style:
+
+ I get 10 times more traffic from [Google](http://google.com/ "Google")
+ than from [Yahoo](http://search.yahoo.com/ "Yahoo Search") or
+ [MSN](http://search.msn.com/ "MSN Search").
+
+The point of reference-style links is not that they're easier to
+write. The point is that with reference-style links, your document
+source is vastly more readable. Compare the above examples: using
+reference-style links, the paragraph itself is only 81 characters
+long; with inline-style links, it's 176 characters; and as raw HTML,
+it's 234 characters. In the raw HTML, there's more markup than there
+is text.
+
+With Markdown's reference-style links, a source document much more
+closely resembles the final output, as rendered in a browser. By
+allowing you to move the markup-related metadata out of the paragraph,
+you can add links without interrupting the narrative flow of your
+prose.
+
+
+<h3 id="em">Emphasis</h3>
+
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `<em>` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`<strong>` tag. E.g., this input:
+
+ *single asterisks*
+
+ _single underscores_
+
+ **double asterisks**
+
+ __double underscores__
+
+will produce:
+
+ <em>single asterisks</em>
+
+ <em>single underscores</em>
+
+ <strong>double asterisks</strong>
+
+ <strong>double underscores</strong>
+
+You can use whichever style you prefer; the lone restriction is that
+the same character must be used to open and close an emphasis span.
+
+Emphasis can be used in the middle of a word:
+
+ un*fucking*believable
+
+But if you surround an `*` or `_` with spaces, it'll be treated as a
+literal asterisk or underscore.
+
+To produce a literal asterisk or underscore at a position where it
+would otherwise be used as an emphasis delimiter, you can backslash
+escape it:
+
+ \*this text is surrounded by literal asterisks\*
+
+
+
+<h3 id="code">Code</h3>
+
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+
+ Use the `printf()` function.
+
+will produce:
+
+ <p>Use the <code>printf()</code> function.</p>
+
+To include a literal backtick character within a code span, you can use
+multiple backticks as the opening and closing delimiters:
+
+ ``There is a literal backtick (`) here.``
+
+which will produce this:
+
+ <p><code>There is a literal backtick (`) here.</code></p>
+
+The backtick delimiters surrounding a code span may include spaces --
+one after the opening, one before the closing. This allows you to place
+literal backtick characters at the beginning or end of a code span:
+
+ A single backtick in a code span: `` ` ``
+
+ A backtick-delimited string in a code span: `` `foo` ``
+
+will produce:
+
+ <p>A single backtick in a code span: <code>`</code></p>
+
+ <p>A backtick-delimited string in a code span: <code>`foo`</code></p>
+
+With a code span, ampersands and angle brackets are encoded as HTML
+entities automatically, which makes it easy to include example HTML
+tags. Markdown will turn this:
+
+ Please don't use any `<blink>` tags.
+
+into:
+
+ <p>Please don't use any <code><blink></code> tags.</p>
+
+You can write this:
+
+ `—` is the decimal-encoded equivalent of `—`.
+
+to produce:
+
+ <p><code>&#8212;</code> is the decimal-encoded
+ equivalent of <code>&mdash;</code>.</p>
+
+
+
+<h3 id="img">Images</h3>
+
+Admittedly, it's fairly difficult to devise a "natural" syntax for
+placing images into a plain text document format.
+
+Markdown uses an image syntax that is intended to resemble the syntax
+for links, allowing for two styles: *inline* and *reference*.
+
+Inline image syntax looks like this:
+
+ 
+
+ 
+
+That is:
+
+* An exclamation mark: `!`;
+* followed by a set of square brackets, containing the `alt`
+ attribute text for the image;
+* followed by a set of parentheses, containing the URL or path to
+ the image, and an optional `title` attribute enclosed in double
+ or single quotes.
+
+Reference-style image syntax looks like this:
+
+ ![Alt text][id]
+
+Where "id" is the name of a defined image reference. Image references
+are defined using syntax identical to link references:
+
+ [id]: url/to/image "Optional title attribute"
+
+As of this writing, Markdown has no syntax for specifying the
+dimensions of an image; if this is important to you, you can simply
+use regular HTML `<img>` tags.
+
+
+* * *
+
+
+<h2 id="misc">Miscellaneous</h2>
+
+<h3 id="autolink">Automatic Links</h3>
+
+Markdown supports a shortcut style for creating "automatic" links for URLs and email addresses: simply surround the URL or email address with angle brackets. What this means is that if you want to show the actual text of a URL or email address, and also have it be a clickable link, you can do this:
+
+ <http://example.com/>
+
+Markdown will turn this into:
+
+ <a href="http://example.com/">http://example.com/</a>
+
+Automatic links for email addresses work similarly, except that
+Markdown will also perform a bit of randomized decimal and hex
+entity-encoding to help obscure your address from address-harvesting
+spambots. For example, Markdown will turn this:
+
+ <address@example.com>
+
+into something like this:
+
+ <a href="mailto:addre
+ ss@example.co
+ m">address@exa
+ mple.com</a>
+
+which will render in a browser as a clickable link to "address@example.com".
+
+(This sort of entity-encoding trick will indeed fool many, if not
+most, address-harvesting bots, but it definitely won't fool all of
+them. It's better than nothing, but an address published in this way
+will probably eventually start receiving spam.)
+
+
+
+<h3 id="backslash">Backslash Escapes</h3>
+
+Markdown allows you to use backslash escapes to generate literal
+characters which would otherwise have special meaning in Markdown's
+formatting syntax. For example, if you wanted to surround a word with
+literal asterisks (instead of an HTML `<em>` tag), you can backslashes
+before the asterisks, like this:
+
+ \*literal asterisks\*
+
+Markdown provides backslash escapes for the following characters:
+
+ \ backslash
+ ` backtick
+ * asterisk
+ _ underscore
+ {} curly braces
+ [] square brackets
+ () parentheses
+ # hash mark
+ + plus sign
+ - minus sign (hyphen)
+ . dot
+ ! exclamation mark
+
--- /dev/null
+> foo
+>
+> > bar
+>
+> foo
--- /dev/null
+## Unordered
+
+Asterisks tight:
+
+* asterisk 1
+* asterisk 2
+* asterisk 3
+
+
+Asterisks loose:
+
+* asterisk 1
+
+* asterisk 2
+
+* asterisk 3
+
+* * *
+
+Pluses tight:
+
++ Plus 1
++ Plus 2
++ Plus 3
+
+
+Pluses loose:
+
++ Plus 1
+
++ Plus 2
+
++ Plus 3
+
+* * *
+
+
+Minuses tight:
+
+- Minus 1
+- Minus 2
+- Minus 3
+
+
+Minuses loose:
+
+- Minus 1
+
+- Minus 2
+
+- Minus 3
+
+
+## Ordered
+
+Tight:
+
+1. First
+2. Second
+3. Third
+
+and:
+
+1. One
+2. Two
+3. Three
+
+
+Loose using tabs:
+
+1. First
+
+2. Second
+
+3. Third
+
+and using spaces:
+
+1. One
+
+2. Two
+
+3. Three
+
+Multiple paragraphs:
+
+1. Item 1, graf one.
+
+ Item 2. graf two. The quick brown fox jumped over the lazy dog's
+ back.
+
+2. Item 2.
+
+3. Item 3.
+
+
+
+## Nested
+
+* Tab
+ * Tab
+ * Tab
+
+Here's another:
+
+1. First
+2. Second:
+ * Fee
+ * Fie
+ * Foe
+3. Third
+
+Same thing but with paragraphs:
+
+1. First
+
+2. Second:
+ * Fee
+ * Fie
+ * Foe
+
+3. Third
+
+
+This was an error in Markdown 1.0.1:
+
+* this
+
+ * sub
+
+ that
--- /dev/null
+***This is strong and em.***
+
+So is ***this*** word.
+
+___This is strong and em.___
+
+So is ___this___ word.
--- /dev/null
++ this is a list item
+ indented with tabs
+
++ this is a list item
+ indented with spaces
+
+Code:
+
+ this code block is indented by one tab
+
+And:
+
+ this code block is indented by two tabs
+
+And:
+
+ + this is an example list item
+ indented with tabs
+
+ + this is an example list item
+ indented with spaces
--- /dev/null
+> A list within a blockquote:
+>
+> * asterisk 1
+> * asterisk 2
+> * asterisk 3
)
-class TestCodeHilite(TestCaseWithAssertStartsWith):
- """ Test codehilite extension. """
-
- def setUp(self):
- self.has_pygments = True
- try:
- import pygments # noqa
- except ImportError:
- self.has_pygments = False
-
- def testBasicCodeHilite(self):
- text = '\t# A Code Comment'
- md = markdown.Markdown(extensions=['codehilite'])
- if self.has_pygments:
- # Pygments can use random lexer here as we did not specify the language
- self.assertStartsWith('<div class="codehilite"><pre>', md.convert(text))
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code># A Code Comment'
- '</code></pre>'
- )
-
- def testLinenumsTrue(self):
- text = '\t# A Code Comment'
- md = markdown.Markdown(
- extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=True)])
- if self.has_pygments:
- # Different versions of pygments output slightly different markup.
- # So we use 'startwith' and test just enough to confirm that
- # pygments received and processed linenums.
- self.assertStartsWith(
- '<table class="codehilitetable"><tr><td class="linenos">',
- md.convert(text)
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="linenums"># A Code Comment'
- '</code></pre>'
- )
-
- def testLinenumsFalse(self):
- text = '\t#!Python\n\t# A Code Comment'
- md = markdown.Markdown(
- extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=False)])
- if self.has_pygments:
- self.assertStartsWith('<div class="codehilite"><pre><span', md.convert(text))
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-python"># A Code Comment'
- '</code></pre>'
- )
-
- def testLinenumsNone(self):
- text = '\t# A Code Comment'
- md = markdown.Markdown(
- extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)])
- if self.has_pygments:
- # Pygments can use random lexer here as we did not specify the language
- self.assertStartsWith('<div class="codehilite"><pre>', md.convert(text))
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code># A Code Comment'
- '</code></pre>'
- )
-
- def testLinenumsNoneWithShebang(self):
- text = '\t#!Python\n\t# A Code Comment'
- md = markdown.Markdown(
- extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)])
- if self.has_pygments:
- # Differant versions of pygments output slightly different markup.
- # So we use 'startwith' and test just enough to confirm that
- # pygments received and processed linenums.
- self.assertStartsWith(
- '<table class="codehilitetable"><tr><td class="linenos">',
- md.convert(text)
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-python linenums"># A Code Comment'
- '</code></pre>'
- )
-
- def testLinenumsNoneWithColon(self):
- text = '\t:::Python\n\t# A Code Comment'
- md = markdown.Markdown(
- extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)]
- )
- if self.has_pygments:
- self.assertStartsWith('<div class="codehilite"><pre><span', md.convert(text))
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-python"># A Code Comment'
- '</code></pre>'
- )
-
- def testHighlightLinesWithColon(self):
- # Test with hl_lines delimited by single or double quotes.
- text0 = '\t:::Python hl_lines="1"\n\t#line 1\n\t#line 2\n\t#line 3'
- text1 = "\t:::Python hl_lines='1'\n\t#line 1\n\t#line 2\n\t#line 3"
-
- for text in (text0, text1):
- md = markdown.Markdown(extensions=['codehilite'])
- if self.has_pygments:
- self.assertStartsWith(
- '<div class="codehilite"><pre><code><span class="hll"',
- md.convert(text).replace('<span></span>', '')
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite">'
- '<code class="language-python">#line 1\n'
- '#line 2\n'
- '#line 3</code></pre>'
- )
-
- def testUsePygmentsFalse(self):
- text = '\t:::Python\n\t# A Code Comment'
- md = markdown.Markdown(
- extensions=[markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False)]
- )
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-python"># A Code Comment'
- '</code></pre>'
- )
-
- def testDoubleEscape(self):
- """ Test entity escape logic in indented code blocks. """
-
- text = '\t:::html\n\t<span>This&That</span>'
- md = markdown.Markdown(
- extensions=[markdown.extensions.codehilite.CodeHiliteExtension()]
- )
- if self.has_pygments:
- self.assertEqual(
- md.convert(text),
- '<div class="codehilite"><pre>'
- '<span></span>'
- '<code><span class="p"><</span><span class="nt">span</span><span class="p">></span>'
- 'This<span class="ni">&amp;</span>That'
- '<span class="p"></</span><span class="nt">span</span><span class="p">></span>'
- '\n</code></pre></div>'
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-html">'
- '<span>This&amp;That</span>'
- '</code></pre>'
- )
-
- def testHighlightAmps(self):
- """ Test amp conversion logic. """
-
- text = '\t:::text\n\t&\n\t&\n\t&amp;'
- md = markdown.Markdown(
- extensions=[markdown.extensions.codehilite.CodeHiliteExtension()]
- )
- if self.has_pygments:
- self.assertEqual(
- md.convert(text),
- '<div class="codehilite"><pre><span></span><code>&\n&amp;\n&amp;amp;\n</code></pre></div>'
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-text">&\n&amp;\n&amp;amp;</code></pre>'
- )
-
-
-class TestFencedCode(TestCaseWithAssertStartsWith):
- """ Test fenced_code extension. """
-
- def setUp(self):
- self.md = markdown.Markdown(extensions=['fenced_code'])
- self.has_pygments = True
- try:
- import pygments # noqa
- except ImportError:
- self.has_pygments = False
-
- def testBasicFence(self):
- """ Test Fenced Code Blocks. """
- text = '''
-A paragraph before a fenced code block:
-
-~~~
-Fenced code block
-~~~'''
- self.assertEqual(
- self.md.convert(text),
- '<p>A paragraph before a fenced code block:</p>\n'
- '<pre><code>Fenced code block\n'
- '</code></pre>'
- )
-
- def testSafeFence(self):
- """ Test Fenced Code with safe_mode. """
- text = '~~~\nCode\n~~~'
- self.md.safeMode = 'replace'
- self.assertEqual(
- self.md.convert(text),
- '<pre><code>Code\n'
- '</code></pre>'
- )
-
- def testNestedFence(self):
- """ Test nested fence. """
-
- text = '''
-~~~~~~~~
-
-~~~~
-~~~~~~~~'''
- self.assertEqual(
- self.md.convert(text),
- '<pre><code>\n'
- '~~~~\n'
- '</code></pre>'
- )
-
- def testFencedLanguage(self):
- """ Test Language Tags. """
-
- text = '''
-~~~~{.python}
-# Some python code
-~~~~'''
- self.assertEqual(
- self.md.convert(text),
- '<pre><code class="python"># Some python code\n'
- '</code></pre>'
- )
-
- def testFencedBackticks(self):
- """ Test Code Fenced with Backticks. """
-
- text = '''
-`````
-# Arbitrary code
-~~~~~ # these tildes will not close the block
-`````'''
- self.assertEqual(
- self.md.convert(text),
- '<pre><code># Arbitrary code\n'
- '~~~~~ # these tildes will not close the block\n'
- '</code></pre>'
- )
-
- def testFencedCodeWithHighlightLines(self):
- """ Test Fenced Code with Highlighted Lines. """
-
- text = '''
-```hl_lines="1 3"
-line 1
-line 2
-line 3
-```'''
- md = markdown.Markdown(
- extensions=[
- markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
- 'fenced_code'
- ]
- )
-
- if self.has_pygments:
- self.assertStartsWith(
- '<div class="codehilite"><pre><code><span class="hll"',
- md.convert(text).replace('<span></span>', '')
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code>line 1\n'
- 'line 2\n'
- 'line 3</code></pre>'
- )
-
- def testFencedLanguageAndHighlightLines(self):
- """ Test Fenced Code with Highlighted Lines. """
-
- text0 = '''
-```.python hl_lines="1 3"
-#line 1
-#line 2
-#line 3
-```'''
- text1 = '''
-~~~{.python hl_lines='1 3'}
-#line 1
-#line 2
-#line 3
-~~~'''
- for text in (text0, text1):
- md = markdown.Markdown(
- extensions=[
- markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
- 'fenced_code'
- ]
- )
- if self.has_pygments:
- self.assertStartsWith(
- '<div class="codehilite"><pre><code><span class="hll"',
- md.convert(text).replace('<span></span>', '')
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-python">#line 1\n'
- '#line 2\n'
- '#line 3</code></pre>'
- )
-
- def testFencedLanguageAndPygmentsDisabled(self):
- """ Test if fenced_code honors CodeHilite option use_pygments=False. """
-
- text = '```python\nfrom __future__ import braces\n```'
- md = markdown.Markdown(
- extensions=[
- markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False),
- 'fenced_code'
- ]
- )
- self.assertIn('<code class="language-python">', md.convert(text))
-
- def testFencedLanguageDoubleEscape(self):
- """ Test entity escape logic in fences. """
-
- text = '```html\n<span>This&That</span>\n```'
- md = markdown.Markdown(
- extensions=[
- markdown.extensions.codehilite.CodeHiliteExtension(),
- 'fenced_code'
- ]
- )
- if self.has_pygments:
- self.assertEqual(
- md.convert(text),
- '<div class="codehilite"><pre>'
- '<span></span><code>'
- '<span class="p"><</span><span class="nt">span</span><span class="p">></span>'
- 'This<span class="ni">&amp;</span>That'
- '<span class="p"></</span><span class="nt">span</span><span class="p">></span>'
- '\n</code></pre></div>'
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-html">'
- '<span>This&amp;That</span>'
- '</code></pre>'
- )
-
- def testFencedAmps(self):
- """ Test amp conversion. """
-
- text = '```text\n&\n&\n&amp;\n```'
- md = markdown.Markdown(
- extensions=[
- markdown.extensions.codehilite.CodeHiliteExtension(),
- 'fenced_code'
- ]
- )
- if self.has_pygments:
- self.assertEqual(
- md.convert(text),
- '<div class="codehilite"><pre><span></span><code>&\n&amp;\n&amp;amp;\n</code></pre></div>'
- )
- else:
- self.assertEqual(
- md.convert(text),
- '<pre class="codehilite"><code class="language-text">&\n&amp;\n&amp;amp;</code></pre>'
- )
-
-
class TestMetaData(unittest.TestCase):
""" Test MetaData extension. """
wikilinks = Kwargs(extensions=['wikilinks'])
- fenced_code = Kwargs(extensions=['fenced_code'])
-
github_flavored = Kwargs(extensions=['fenced_code'])
sane_lists = Kwargs(extensions=['sane_lists'])
tables = Kwargs(extensions=['tables'])
- tables_and_attr_list = Kwargs(extensions=['tables', 'attr_list'])
-
extra_config = Kwargs(
extensions=['extra'],
extension_configs={
--- /dev/null
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2020 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase, recursionlimit
+
+
+class TestBlockquoteBlocks(TestCase):
+
+ # TODO: Move legacy tests here
+
+ def test_nesting_limit(self):
+ # Test that the nesting limit is within 100 levels of recursion limit. Future code changes could cause the
+ # recursion limit to need adjusted here. We need to acocunt for all of Markdown's internal calls. Finally, we
+ # need to account for the 100 level cushion which we are testing.
+ with recursionlimit(120):
+ self.assertMarkdownRenders(
+ '>>>>>>>>>>',
+ self.dedent(
+ """
+ <blockquote>
+ <blockquote>
+ <blockquote>
+ <blockquote>
+ <blockquote>
+ <p>>>>>></p>
+ </blockquote>
+ </blockquote>
+ </blockquote>
+ </blockquote>
+ </blockquote>
+ """
+ )
+ )
)
)
+ def test_hr_after_emstrong(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ***text***
+ ***
+ """
+ ),
+ self.dedent(
+ """
+ <p><strong><em>text</em></strong></p>
+ <hr />
+ """
+ )
+ )
+
def test_not_hr_2_asterisks(self):
self.assertMarkdownRenders(
'**',
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestHTMLBlocks(TestCase):
+
+ def test_raw_paragraph(self):
+ self.assertMarkdownRenders(
+ '<p>A raw paragraph.</p>',
+ '<p>A raw paragraph.</p>'
+ )
+
+ def test_raw_skip_inline_markdown(self):
+ self.assertMarkdownRenders(
+ '<p>A *raw* paragraph.</p>',
+ '<p>A *raw* paragraph.</p>'
+ )
+
+ def test_raw_indent_one_space(self):
+ self.assertMarkdownRenders(
+ ' <p>A *raw* paragraph.</p>',
+ '<p>A *raw* paragraph.</p>'
+ )
+
+ def test_raw_indent_two_spaces(self):
+ self.assertMarkdownRenders(
+ ' <p>A *raw* paragraph.</p>',
+ '<p>A *raw* paragraph.</p>'
+ )
+
+ def test_raw_indent_three_spaces(self):
+ self.assertMarkdownRenders(
+ ' <p>A *raw* paragraph.</p>',
+ '<p>A *raw* paragraph.</p>'
+ )
+
+ def test_raw_indent_four_spaces(self):
+ self.assertMarkdownRenders(
+ ' <p>code block</p>',
+ self.dedent(
+ """
+ <pre><code><p>code block</p>
+ </code></pre>
+ """
+ )
+ )
+
+ def test_raw_span(self):
+ self.assertMarkdownRenders(
+ '<span>*inline*</span>',
+ '<p><span><em>inline</em></span></p>'
+ )
+
+ def test_code_span(self):
+ self.assertMarkdownRenders(
+ '`<p>code span</p>`',
+ '<p><code><p>code span</p></code></p>'
+ )
+
+ def test_code_span_open_gt(self):
+ self.assertMarkdownRenders(
+ '*bar* `<` *foo*',
+ '<p><em>bar</em> <code><</code> <em>foo</em></p>'
+ )
+
+ def test_raw_empty(self):
+ self.assertMarkdownRenders(
+ '<p></p>',
+ '<p></p>'
+ )
+
+ def test_raw_empty_space(self):
+ self.assertMarkdownRenders(
+ '<p> </p>',
+ '<p> </p>'
+ )
+
+ def test_raw_empty_newline(self):
+ self.assertMarkdownRenders(
+ '<p>\n</p>',
+ '<p>\n</p>'
+ )
+
+ def test_raw_empty_blank_line(self):
+ self.assertMarkdownRenders(
+ '<p>\n\n</p>',
+ '<p>\n\n</p>'
+ )
+
+ def test_raw_uppercase(self):
+ self.assertMarkdownRenders(
+ '<DIV>*foo*</DIV>',
+ '<DIV>*foo*</DIV>'
+ )
+
+ def test_raw_uppercase_multiline(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <DIV>
+ *foo*
+ </DIV>
+ """
+ ),
+ self.dedent(
+ """
+ <DIV>
+ *foo*
+ </DIV>
+ """
+ )
+ )
+
+ def test_multiple_raw_single_line(self):
+ self.assertMarkdownRenders(
+ '<p>*foo*</p><div>*bar*</div>',
+ self.dedent(
+ """
+ <p>*foo*</p>
+ <div>*bar*</div>
+ """
+ )
+ )
+
+ def test_multiple_raw_single_line_with_pi(self):
+ self.assertMarkdownRenders(
+ "<p>*foo*</p><?php echo '>'; ?>",
+ self.dedent(
+ """
+ <p>*foo*</p>
+ <?php echo '>'; ?>
+ """
+ )
+ )
+
+ def test_multiline_raw(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p>
+ A raw paragraph
+ with multiple lines.
+ </p>
+ """
+ ),
+ self.dedent(
+ """
+ <p>
+ A raw paragraph
+ with multiple lines.
+ </p>
+ """
+ )
+ )
+
+ def test_blank_lines_in_raw(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p>
+
+ A raw paragraph...
+
+ with many blank lines.
+
+ </p>
+ """
+ ),
+ self.dedent(
+ """
+ <p>
+
+ A raw paragraph...
+
+ with many blank lines.
+
+ </p>
+ """
+ )
+ )
+
+ def test_raw_surrounded_by_Markdown(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ Some *Markdown* text.
+
+ <p>*Raw* HTML.</p>
+
+ More *Markdown* text.
+ """
+ ),
+ self.dedent(
+ """
+ <p>Some <em>Markdown</em> text.</p>
+ <p>*Raw* HTML.</p>
+
+ <p>More <em>Markdown</em> text.</p>
+ """
+ )
+ )
+
+ def test_raw_surrounded_by_text_without_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ Some *Markdown* text.
+ <p>*Raw* HTML.</p>
+ More *Markdown* text.
+ """
+ ),
+ self.dedent(
+ """
+ <p>Some <em>Markdown</em> text.</p>
+ <p>*Raw* HTML.</p>
+ <p>More <em>Markdown</em> text.</p>
+ """
+ )
+ )
+
+ def test_multiline_markdown_with_code_span(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ A paragraph with a block-level
+ `<p>code span</p>`, which is
+ at the start of a line.
+ """
+ ),
+ self.dedent(
+ """
+ <p>A paragraph with a block-level
+ <code><p>code span</p></code>, which is
+ at the start of a line.</p>
+ """
+ )
+ )
+
+ def test_raw_block_preceded_by_markdown_code_span_with_unclosed_block_tag(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ A paragraph with a block-level code span: `<div>`.
+
+ <p>*not markdown*</p>
+
+ This is *markdown*
+ """
+ ),
+ self.dedent(
+ """
+ <p>A paragraph with a block-level code span: <code><div></code>.</p>
+ <p>*not markdown*</p>
+
+ <p>This is <em>markdown</em></p>
+ """
+ )
+ )
+
+ def test_raw_one_line_followed_by_text(self):
+ self.assertMarkdownRenders(
+ '<p>*foo*</p>*bar*',
+ self.dedent(
+ """
+ <p>*foo*</p>
+ <p><em>bar</em></p>
+ """
+ )
+ )
+
+ def test_raw_one_line_followed_by_span(self):
+ self.assertMarkdownRenders(
+ "<p>*foo*</p><span>*bar*</span>",
+ self.dedent(
+ """
+ <p>*foo*</p>
+ <p><span><em>bar</em></span></p>
+ """
+ )
+ )
+
+ def test_raw_with_markdown_blocks(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+ Not a Markdown paragraph.
+
+ * Not a list item.
+ * Another non-list item.
+
+ Another non-Markdown paragraph.
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ Not a Markdown paragraph.
+
+ * Not a list item.
+ * Another non-list item.
+
+ Another non-Markdown paragraph.
+ </div>
+ """
+ )
+ )
+
+ def test_adjacent_raw_blocks(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p>A raw paragraph.</p>
+ <p>A second raw paragraph.</p>
+ """
+ ),
+ self.dedent(
+ """
+ <p>A raw paragraph.</p>
+ <p>A second raw paragraph.</p>
+ """
+ )
+ )
+
+ def test_adjacent_raw_blocks_with_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p>A raw paragraph.</p>
+
+ <p>A second raw paragraph.</p>
+ """
+ ),
+ self.dedent(
+ """
+ <p>A raw paragraph.</p>
+
+ <p>A second raw paragraph.</p>
+ """
+ )
+ )
+
+ def test_nested_raw_one_line(self):
+ self.assertMarkdownRenders(
+ '<div><p>*foo*</p></div>',
+ '<div><p>*foo*</p></div>'
+ )
+
+ def test_nested_raw_block(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+ <p>A raw paragraph.</p>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A raw paragraph.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_nested_indented_raw_block(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+ <p>A raw paragraph.</p>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A raw paragraph.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_nested_raw_blocks(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+ <p>A raw paragraph.</p>
+ <p>A second raw paragraph.</p>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A raw paragraph.</p>
+ <p>A second raw paragraph.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_nested_raw_blocks_with_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+
+ <p>A raw paragraph.</p>
+
+ <p>A second raw paragraph.</p>
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+
+ <p>A raw paragraph.</p>
+
+ <p>A second raw paragraph.</p>
+
+ </div>
+ """
+ )
+ )
+
+ def test_nested_inline_one_line(self):
+ self.assertMarkdownRenders(
+ '<p><em>foo</em><br></p>',
+ '<p><em>foo</em><br></p>'
+ )
+
+ def test_raw_nested_inline(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+ <p>
+ <span>*text*</span>
+ </p>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>
+ <span>*text*</span>
+ </p>
+ </div>
+ """
+ )
+ )
+
+ def test_raw_nested_inline_with_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+
+ <p>
+
+ <span>*text*</span>
+
+ </p>
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+
+ <p>
+
+ <span>*text*</span>
+
+ </p>
+
+ </div>
+ """
+ )
+ )
+
+ def test_raw_html5(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <section>
+ <header>
+ <hgroup>
+ <h1>Hello :-)</h1>
+ </hgroup>
+ </header>
+ <figure>
+ <img src="image.png" alt="" />
+ <figcaption>Caption</figcaption>
+ </figure>
+ <footer>
+ <p>Some footer</p>
+ </footer>
+ </section>
+ """
+ ),
+ self.dedent(
+ """
+ <section>
+ <header>
+ <hgroup>
+ <h1>Hello :-)</h1>
+ </hgroup>
+ </header>
+ <figure>
+ <img src="image.png" alt="" />
+ <figcaption>Caption</figcaption>
+ </figure>
+ <footer>
+ <p>Some footer</p>
+ </footer>
+ </section>
+ """
+ )
+ )
+
+ def test_raw_pre_tag(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ Preserve whitespace in raw html
+
+ <pre>
+ class Foo():
+ bar = 'bar'
+
+ @property
+ def baz(self):
+ return self.bar
+ </pre>
+ """
+ ),
+ self.dedent(
+ """
+ <p>Preserve whitespace in raw html</p>
+ <pre>
+ class Foo():
+ bar = 'bar'
+
+ @property
+ def baz(self):
+ return self.bar
+ </pre>
+ """
+ )
+ )
+
+ def test_raw_pre_tag_nested_escaped_html(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <pre>
+ <p>foo</p>
+ </pre>
+ """
+ ),
+ self.dedent(
+ """
+ <pre>
+ <p>foo</p>
+ </pre>
+ """
+ )
+ )
+
+ def test_raw_p_no_end_tag(self):
+ self.assertMarkdownRenders(
+ '<p>*text*',
+ '<p>*text*'
+ )
+
+ def test_raw_multiple_p_no_end_tag(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p>*text*'
+
+ <p>more *text*
+ """
+ ),
+ self.dedent(
+ """
+ <p>*text*'
+
+ <p>more *text*
+ """
+ )
+ )
+
+ def test_raw_p_no_end_tag_followed_by_blank_line(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p>*raw text*'
+
+ Still part of *raw* text.
+ """
+ ),
+ self.dedent(
+ """
+ <p>*raw text*'
+
+ Still part of *raw* text.
+ """
+ )
+ )
+
+ def test_raw_nested_p_no_end_tag(self):
+ self.assertMarkdownRenders(
+ '<div><p>*text*</div>',
+ '<div><p>*text*</div>'
+ )
+
+ def test_raw_open_bracket_only(self):
+ self.assertMarkdownRenders(
+ '<',
+ '<p><</p>'
+ )
+
+ def test_raw_open_bracket_followed_by_space(self):
+ self.assertMarkdownRenders(
+ '< foo',
+ '<p>< foo</p>'
+ )
+
+ def test_raw_missing_close_bracket(self):
+ self.assertMarkdownRenders(
+ '<foo',
+ '<p><foo</p>'
+ )
+
+ def test_raw_attributes(self):
+ self.assertMarkdownRenders(
+ '<p id="foo", class="bar baz", style="margin: 15px; line-height: 1.5; text-align: center;">text</p>',
+ '<p id="foo", class="bar baz", style="margin: 15px; line-height: 1.5; text-align: center;">text</p>'
+ )
+
+ def test_raw_attributes_nested(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div id="foo, class="bar", style="background: #ffe7e8; border: 2px solid #e66465;">
+ <p id="baz", style="margin: 15px; line-height: 1.5; text-align: center;">
+ <img scr="../foo.jpg" title="with 'quoted' text." valueless_attr weirdness="<i>foo</i>" />
+ </p>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div id="foo, class="bar", style="background: #ffe7e8; border: 2px solid #e66465;">
+ <p id="baz", style="margin: 15px; line-height: 1.5; text-align: center;">
+ <img scr="../foo.jpg" title="with 'quoted' text." valueless_attr weirdness="<i>foo</i>" />
+ </p>
+ </div>
+ """
+ )
+ )
+
+ def test_raw_comment_one_line(self):
+ self.assertMarkdownRenders(
+ '<!-- *foo* -->',
+ '<!-- *foo* -->'
+ )
+
+ def test_raw_comment_one_line_with_tag(self):
+ self.assertMarkdownRenders(
+ '<!-- <tag> -->',
+ '<!-- <tag> -->'
+ )
+
+ def test_comment_in_code_span(self):
+ self.assertMarkdownRenders(
+ '`<!-- *foo* -->`',
+ '<p><code><!-- *foo* --></code></p>'
+ )
+
+ def test_raw_comment_one_line_followed_by_text(self):
+ self.assertMarkdownRenders(
+ '<!-- *foo* -->*bar*',
+ self.dedent(
+ """
+ <!-- *foo* -->
+ <p><em>bar</em></p>
+ """
+ )
+ )
+
+ def test_raw_comment_one_line_followed_by_html(self):
+ self.assertMarkdownRenders(
+ '<!-- *foo* --><p>*bar*</p>',
+ self.dedent(
+ """
+ <!-- *foo* -->
+ <p>*bar*</p>
+ """
+ )
+ )
+
+ # Note: Trailing (insignificant) whitespace is not preserved, which does not match the
+ # reference implementation. However, it is not a change in behavior for Python-Markdown.
+ def test_raw_comment_trailing_whitespace(self):
+ self.assertMarkdownRenders(
+ '<!-- *foo* --> ',
+ '<!-- *foo* -->'
+ )
+
+ # Note: this is a change in behavior for Python-Markdown, which does *not* match the reference
+ # implementation. However, it does match the HTML5 spec. Declarations must start with either
+ # `<!DOCTYPE` or `<![`. Anything else that starts with `<!` is a comment. According to the
+ # HTML5 spec, a comment without the hyphens is a "bogus comment", but a comment nonetheless.
+ # See https://www.w3.org/TR/html52/syntax.html#markup-declaration-open-state.
+ # If we wanted to change this behavior, we could override `HTMLParser.parse_bogus_comment()`.
+ def test_bogus_comment(self):
+ self.assertMarkdownRenders(
+ '<!*foo*>',
+ '<!--*foo*-->'
+ )
+
+ def test_raw_multiline_comment(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!--
+ *foo*
+ -->
+ """
+ ),
+ self.dedent(
+ """
+ <!--
+ *foo*
+ -->
+ """
+ )
+ )
+
+ def test_raw_multiline_comment_with_tag(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!--
+ <tag>
+ -->
+ """
+ ),
+ self.dedent(
+ """
+ <!--
+ <tag>
+ -->
+ """
+ )
+ )
+
+ def test_raw_multiline_comment_first_line(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!-- *foo*
+ -->
+ """
+ ),
+ self.dedent(
+ """
+ <!-- *foo*
+ -->
+ """
+ )
+ )
+
+ def test_raw_multiline_comment_last_line(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!--
+ *foo* -->
+ """
+ ),
+ self.dedent(
+ """
+ <!--
+ *foo* -->
+ """
+ )
+ )
+
+ def test_raw_comment_with_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!--
+
+ *foo*
+
+ -->
+ """
+ ),
+ self.dedent(
+ """
+ <!--
+
+ *foo*
+
+ -->
+ """
+ )
+ )
+
+ def test_raw_comment_with_blank_lines_with_tag(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!--
+
+ <tag>
+
+ -->
+ """
+ ),
+ self.dedent(
+ """
+ <!--
+
+ <tag>
+
+ -->
+ """
+ )
+ )
+
+ def test_raw_comment_with_blank_lines_first_line(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!-- *foo*
+
+ -->
+ """
+ ),
+ self.dedent(
+ """
+ <!-- *foo*
+
+ -->
+ """
+ )
+ )
+
+ def test_raw_comment_with_blank_lines_last_line(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!--
+
+ *foo* -->
+ """
+ ),
+ self.dedent(
+ """
+ <!--
+
+ *foo* -->
+ """
+ )
+ )
+
+ def test_raw_comment_indented(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!--
+
+ *foo*
+
+ -->
+ """
+ ),
+ self.dedent(
+ """
+ <!--
+
+ *foo*
+
+ -->
+ """
+ )
+ )
+
+ def test_raw_comment_indented_with_tag(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!--
+
+ <tag>
+
+ -->
+ """
+ ),
+ self.dedent(
+ """
+ <!--
+
+ <tag>
+
+ -->
+ """
+ )
+ )
+
+ def test_raw_comment_nested(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+ <!-- *foo* -->
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <!-- *foo* -->
+ </div>
+ """
+ )
+ )
+
+ def test_comment_in_code_block(self):
+ self.assertMarkdownRenders(
+ ' <!-- *foo* -->',
+ self.dedent(
+ """
+ <pre><code><!-- *foo* -->
+ </code></pre>
+ """
+ )
+ )
+
+ # Note: This is a change in behavior. Previously, Python-Markdown interpreted this in the same manner
+ # as browsers and all text after the opening comment tag was considered to be in a comment. However,
+ # that did not match the reference implementation. The new behavior does.
+ def test_unclosed_comment_(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!-- unclosed comment
+
+ *not* a comment
+ """
+ ),
+ self.dedent(
+ """
+ <p><!-- unclosed comment</p>
+ <p><em>not</em> a comment</p>
+ """
+ )
+ )
+
+ def test_raw_processing_instruction_one_line(self):
+ self.assertMarkdownRenders(
+ "<?php echo '>'; ?>",
+ "<?php echo '>'; ?>"
+ )
+
+ # This is a change in behavior and does not match the reference implementation.
+ # We have no way to determine if text is on the same line, so we get this. TODO: reevaluate!
+ def test_raw_processing_instruction_one_line_followed_by_text(self):
+ self.assertMarkdownRenders(
+ "<?php echo '>'; ?>*bar*",
+ self.dedent(
+ """
+ <?php echo '>'; ?>
+ <p><em>bar</em></p>
+ """
+ )
+ )
+
+ def test_raw_multiline_processing_instruction(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <?php
+ echo '>';
+ ?>
+ """
+ ),
+ self.dedent(
+ """
+ <?php
+ echo '>';
+ ?>
+ """
+ )
+ )
+
+ def test_raw_processing_instruction_with_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <?php
+
+ echo '>';
+
+ ?>
+ """
+ ),
+ self.dedent(
+ """
+ <?php
+
+ echo '>';
+
+ ?>
+ """
+ )
+ )
+
+ def test_raw_processing_instruction_indented(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <?php
+
+ echo '>';
+
+ ?>
+ """
+ ),
+ self.dedent(
+ """
+ <?php
+
+ echo '>';
+
+ ?>
+ """
+ )
+ )
+
+ def test_raw_declaration_one_line(self):
+ self.assertMarkdownRenders(
+ '<!DOCTYPE html>',
+ '<!DOCTYPE html>'
+ )
+
+ # This is a change in behavior and does not match the reference implementation.
+ # We have no way to determine if text is on the same line, so we get this. TODO: reevaluate!
+ def test_raw_declaration_one_line_followed_by_text(self):
+ self.assertMarkdownRenders(
+ '<!DOCTYPE html>*bar*',
+ self.dedent(
+ """
+ <!DOCTYPE html>
+ <p><em>bar</em></p>
+ """
+ )
+ )
+
+ def test_raw_multiline_declaration(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+ """
+ ),
+ self.dedent(
+ """
+ <!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.1//EN"
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+ """
+ )
+ )
+
+ def test_raw_cdata_one_line(self):
+ self.assertMarkdownRenders(
+ '<![CDATA[ document.write(">"); ]]>',
+ '<![CDATA[ document.write(">"); ]]>'
+ )
+
+ # Note: this is a change. Neither previous output nor this match reference implementation.
+ def test_raw_cdata_one_line_followed_by_text(self):
+ self.assertMarkdownRenders(
+ '<![CDATA[ document.write(">"); ]]>*bar*',
+ self.dedent(
+ """
+ <![CDATA[ document.write(">"); ]]>
+ <p><em>bar</em></p>
+ """
+ )
+ )
+
+ def test_raw_multiline_cdata(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <![CDATA[
+ document.write(">");
+ ]]>
+ """
+ ),
+ self.dedent(
+ """
+ <![CDATA[
+ document.write(">");
+ ]]>
+ """
+ )
+ )
+
+ def test_raw_cdata_with_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <![CDATA[
+
+ document.write(">");
+
+ ]]>
+ """
+ ),
+ self.dedent(
+ """
+ <![CDATA[
+
+ document.write(">");
+
+ ]]>
+ """
+ )
+ )
+
+ def test_raw_cdata_indented(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <![CDATA[
+
+ document.write(">");
+
+ ]]>
+ """
+ ),
+ self.dedent(
+ """
+ <![CDATA[
+
+ document.write(">");
+
+ ]]>
+ """
+ )
+ )
+
+ def test_charref(self):
+ self.assertMarkdownRenders(
+ '§',
+ '<p>§</p>'
+ )
+
+ def test_nested_charref(self):
+ self.assertMarkdownRenders(
+ '<p>§</p>',
+ '<p>§</p>'
+ )
+
+ def test_entityref(self):
+ self.assertMarkdownRenders(
+ '§',
+ '<p>§</p>'
+ )
+
+ def test_nested_entityref(self):
+ self.assertMarkdownRenders(
+ '<p>§</p>',
+ '<p>§</p>'
+ )
+
+ def test_amperstand(self):
+ self.assertMarkdownRenders(
+ 'AT&T & AT&T',
+ '<p>AT&T & AT&T</p>'
+ )
+
+ def test_startendtag(self):
+ self.assertMarkdownRenders(
+ '<hr>',
+ '<hr>'
+ )
+
+ def test_startendtag_with_attrs(self):
+ self.assertMarkdownRenders(
+ '<hr id="foo" class="bar">',
+ '<hr id="foo" class="bar">'
+ )
+
+ def test_startendtag_with_space(self):
+ self.assertMarkdownRenders(
+ '<hr >',
+ '<hr >'
+ )
+
+ def test_closed_startendtag(self):
+ self.assertMarkdownRenders(
+ '<hr />',
+ '<hr />'
+ )
+
+ def test_closed_startendtag_without_space(self):
+ self.assertMarkdownRenders(
+ '<hr/>',
+ '<hr/>'
+ )
+
+ def test_closed_startendtag_with_attrs(self):
+ self.assertMarkdownRenders(
+ '<hr id="foo" class="bar" />',
+ '<hr id="foo" class="bar" />'
+ )
+
+ def test_nested_startendtag(self):
+ self.assertMarkdownRenders(
+ '<div><hr></div>',
+ '<div><hr></div>'
+ )
+
+ def test_nested_closed_startendtag(self):
+ self.assertMarkdownRenders(
+ '<div><hr /></div>',
+ '<div><hr /></div>'
+ )
+
+ def test_auto_links_dont_break_parser(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <https://example.com>
+
+ <email@example.com>
+ """
+ ),
+ '<p><a href="https://example.com">https://example.com</a></p>\n'
+ '<p><a href="mailto:em'
+ 'ail@example'
+ '.com">email@e'
+ 'xample.com</a></p>'
+ )
+
+ def test_text_links_ignored(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ https://example.com
+
+ email@example.com
+ """
+ ),
+ self.dedent(
+ """
+ <p>https://example.com</p>
+ <p>email@example.com</p>
+ """
+ ),
+ )
+
+ def text_invalid_tags(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <some [weird](http://example.com) stuff>
+
+ <some>> <<unbalanced>> <<brackets>
+ """
+ ),
+ self.dedent(
+ """
+ <p><some <a href="http://example.com">weird</a> stuff></p>
+ <p><some>> <<unbalanced>> <<brackets></p>
+ """
+ )
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestAbbr(TestCase):
+
+ default_kwargs = {'extensions': ['abbr']}
+
+ def test_abbr_upper(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR
+
+ *[ABBR]: Abbreviation
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="Abbreviation">ABBR</abbr></p>
+ """
+ )
+ )
+
+ def test_abbr_lower(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ abbr
+
+ *[abbr]: Abbreviation
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="Abbreviation">abbr</abbr></p>
+ """
+ )
+ )
+
+ def test_abbr_multiple(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ The HTML specification
+ is maintained by the W3C.
+
+ *[HTML]: Hyper Text Markup Language
+ *[W3C]: World Wide Web Consortium
+ """
+ ),
+ self.dedent(
+ """
+ <p>The <abbr title="Hyper Text Markup Language">HTML</abbr> specification
+ is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>.</p>
+ """
+ )
+ )
+
+ def test_abbr_override(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR
+
+ *[ABBR]: Ignored
+ *[ABBR]: The override
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="The override">ABBR</abbr></p>
+ """
+ )
+ )
+
+ def test_abbr_no_blank_Lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR
+ *[ABBR]: Abbreviation
+ ABBR
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="Abbreviation">ABBR</abbr></p>
+ <p><abbr title="Abbreviation">ABBR</abbr></p>
+ """
+ )
+ )
+
+ def test_abbr_no_space(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR
+
+ *[ABBR]:Abbreviation
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="Abbreviation">ABBR</abbr></p>
+ """
+ )
+ )
+
+ def test_abbr_extra_space(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR
+
+ *[ABBR] : Abbreviation
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="Abbreviation">ABBR</abbr></p>
+ """
+ )
+ )
+
+ def test_abbr_line_break(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR
+
+ *[ABBR]:
+ Abbreviation
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="Abbreviation">ABBR</abbr></p>
+ """
+ )
+ )
+
+ def test_abbr_ignore_unmatched_case(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR abbr
+
+ *[ABBR]: Abbreviation
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="Abbreviation">ABBR</abbr> abbr</p>
+ """
+ )
+ )
+
+ def test_abbr_partial_word(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR ABBREVIATION
+
+ *[ABBR]: Abbreviation
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="Abbreviation">ABBR</abbr> ABBREVIATION</p>
+ """
+ )
+ )
+
+ def test_abbr_unused(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ foo bar
+
+ *[ABBR]: Abbreviation
+ """
+ ),
+ self.dedent(
+ """
+ <p>foo bar</p>
+ """
+ )
+ )
+
+ def test_abbr_double_quoted(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR
+
+ *[ABBR]: "Abbreviation"
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title=""Abbreviation"">ABBR</abbr></p>
+ """
+ )
+ )
+
+ def test_abbr_single_quoted(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ABBR
+
+ *[ABBR]: 'Abbreviation'
+ """
+ ),
+ self.dedent(
+ """
+ <p><abbr title="'Abbreviation'">ABBR</abbr></p>
+ """
+ )
+ )
--- /dev/null
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestAdmonition(TestCase):
+
+ def test_with_lists(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ - List
+
+ !!! note "Admontion"
+
+ - Paragraph
+
+ Paragraph
+ '''
+ ),
+ self.dedent(
+ '''
+ <ul>
+ <li>
+ <p>List</p>
+ <div class="admonition note">
+ <p class="admonition-title">Admontion</p>
+ <ul>
+ <li>
+ <p>Paragraph</p>
+ <p>Paragraph</p>
+ </li>
+ </ul>
+ </div>
+ </li>
+ </ul>
+ '''
+ ),
+ extensions=['admonition']
+ )
+
+ def test_with_big_lists(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ - List
+
+ !!! note "Admontion"
+
+ - Paragraph
+
+ Paragraph
+
+ - Paragraph
+
+ paragraph
+ '''
+ ),
+ self.dedent(
+ '''
+ <ul>
+ <li>
+ <p>List</p>
+ <div class="admonition note">
+ <p class="admonition-title">Admontion</p>
+ <ul>
+ <li>
+ <p>Paragraph</p>
+ <p>Paragraph</p>
+ </li>
+ <li>
+ <p>Paragraph</p>
+ <p>paragraph</p>
+ </li>
+ </ul>
+ </div>
+ </li>
+ </ul>
+ '''
+ ),
+ extensions=['admonition']
+ )
+
+ def test_with_complex_lists(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ - List
+
+ !!! note "Admontion"
+
+ - Paragraph
+
+ !!! note "Admontion"
+
+ 1. Paragraph
+
+ Paragraph
+ '''
+ ),
+ self.dedent(
+ '''
+ <ul>
+ <li>
+ <p>List</p>
+ <div class="admonition note">
+ <p class="admonition-title">Admontion</p>
+ <ul>
+ <li>
+ <p>Paragraph</p>
+ <div class="admonition note">
+ <p class="admonition-title">Admontion</p>
+ <ol>
+ <li>
+ <p>Paragraph</p>
+ <p>Paragraph</p>
+ </li>
+ </ol>
+ </div>
+ </li>
+ </ul>
+ </div>
+ </li>
+ </ul>
+ '''
+ ),
+ extensions=['admonition']
+ )
+
+ def test_definition_list(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ - List
+
+ !!! note "Admontion"
+
+ Term
+
+ : Definition
+
+ More text
+
+ : Another
+ definition
+
+ Even more text
+ '''
+ ),
+ self.dedent(
+ '''
+ <ul>
+ <li>
+ <p>List</p>
+ <div class="admonition note">
+ <p class="admonition-title">Admontion</p>
+ <dl>
+ <dt>Term</dt>
+ <dd>
+ <p>Definition</p>
+ <p>More text</p>
+ </dd>
+ <dd>
+ <p>Another
+ definition</p>
+ <p>Even more text</p>
+ </dd>
+ </dl>
+ </div>
+ </li>
+ </ul>
+ '''
+ ),
+ extensions=['admonition', 'def_list']
+ )
--- /dev/null
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2020 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestAttrList(TestCase):
+
+ maxDiff = None
+
+ # TODO: Move the rest of the attr_list tests here.
+
+ def test_empty_list(self):
+ self.assertMarkdownRenders(
+ '*foo*{ }',
+ '<p><em>foo</em>{ }</p>',
+ extensions=['attr_list']
+ )
+
+ def test_table_td(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ | A { .foo } | *B*{ .foo } | C { } | D{ .foo } | E { .foo } F |
+ |-------------|-------------|-------|---------------|--------------|
+ | a { .foo } | *b*{ .foo } | c { } | d{ .foo } | e { .foo } f |
+ | valid on td | inline | empty | missing space | not at end |
+ """
+ ),
+ self.dedent(
+ """
+ <table>
+ <thead>
+ <tr>
+ <th class="foo">A</th>
+ <th><em class="foo">B</em></th>
+ <th>C { }</th>
+ <th>D{ .foo }</th>
+ <th>E { .foo } F</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="foo">a</td>
+ <td><em class="foo">b</em></td>
+ <td>c { }</td>
+ <td>d{ .foo }</td>
+ <td>e { .foo } f</td>
+ </tr>
+ <tr>
+ <td>valid on td</td>
+ <td>inline</td>
+ <td>empty</td>
+ <td>missing space</td>
+ <td>not at end</td>
+ </tr>
+ </tbody>
+ </table>
+ """
+ ),
+ extensions=['attr_list', 'tables']
+ )
--- /dev/null
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+from markdown.extensions.codehilite import CodeHiliteExtension, CodeHilite
+
+try:
+ import pygments # noqa
+ has_pygments = True
+except ImportError:
+ has_pygments = False
+
+
+class TestCodeHiliteClass(TestCase):
+ """ Test the markdown.extensions.codehilite.CodeHilite class. """
+
+ maxDiff = None
+
+ def assertOutputEquals(self, source, expected, **options):
+ """
+ Test that source code block results in the expected output with given options.
+ """
+
+ output = CodeHilite(source, **options).hilite()
+ self.assertMultiLineEqual(output.strip(), expected)
+
+ def test_codehilite_defaults(self):
+ if has_pygments:
+ # Odd result as no lang given and a single comment is not enough for guessing.
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="err"># A Code Comment</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code># A Code Comment\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('# A Code Comment', expected)
+
+ def test_codehilite_guess_lang(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="cp"><?php</span> '
+ '<span class="k">print</span><span class="p">(</span><span class="s2">"Hello World"</span>'
+ '<span class="p">);</span> <span class="cp">?></span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code><?php print("Hello World"); ?>\n'
+ '</code></pre>'
+ )
+ # Use PHP as the the starting `<?php` tag ensures an accurate guess.
+ self.assertOutputEquals('<?php print("Hello World"); ?>', expected, guess_lang=True)
+
+ def test_codehilite_guess_lang_plain_text(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="err">plain text</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code>plain text\n'
+ '</code></pre>'
+ )
+ # This will be difficult to guess.
+ self.assertOutputEquals('plain text', expected, guess_lang=True)
+
+ def test_codehilite_set_lang(self):
+ if has_pygments:
+ # Note an extra `<span class="x">` is added to end of code block when lang explicitly set.
+ # Compare with expected output for `test_guess_lang`. Not sure why this happens.
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="cp"><?php</span> '
+ '<span class="k">print</span><span class="p">(</span><span class="s2">"Hello World"</span>'
+ '<span class="p">);</span> <span class="cp">?></span><span class="x"></span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-php"><?php print("Hello World"); ?>\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('<?php print("Hello World"); ?>', expected, lang='php')
+
+ def test_codehilite_bad_lang(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="cp"><?php</span> '
+ '<span class="k">print</span><span class="p">(</span><span class="s2">'
+ '"Hello World"</span><span class="p">);</span> <span class="cp">?></span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ # Note that without pygments there is no way to check that the language name is bad.
+ expected = (
+ '<pre class="codehilite"><code class="language-unkown">'
+ '<?php print("Hello World"); ?>\n'
+ '</code></pre>'
+ )
+ # The starting `<?php` tag ensures an accurate guess.
+ self.assertOutputEquals('<?php print("Hello World"); ?>', expected, lang='unkown')
+
+ def test_codehilite_use_pygments_false(self):
+ expected = (
+ '<pre class="codehilite"><code class="language-php"><?php print("Hello World"); ?>\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('<?php print("Hello World"); ?>', expected, lang='php', use_pygments=False)
+
+ def test_codehilite_lang_prefix_empty(self):
+ expected = (
+ '<pre class="codehilite"><code class="php"><?php print("Hello World"); ?>\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals(
+ '<?php print("Hello World"); ?>', expected, lang='php', use_pygments=False, lang_prefix=''
+ )
+
+ def test_codehilite_lang_prefix(self):
+ expected = (
+ '<pre class="codehilite"><code class="lang-php"><?php print("Hello World"); ?>\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals(
+ '<?php print("Hello World"); ?>', expected, lang='php', use_pygments=False, lang_prefix='lang-'
+ )
+
+ def test_codehilite_linenos_true(self):
+ if has_pygments:
+ expected = (
+ '<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div>'
+ '</td><td class="code"><div class="codehilite"><pre><span></span><code>plain text\n'
+ '</code></pre></div>\n'
+ '</td></tr></table>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text linenums">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', linenos=True)
+
+ def test_codehilite_linenos_false(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code>plain text\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', linenos=False)
+
+ def test_codehilite_linenos_none(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code>plain text\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', linenos=None)
+
+ def test_codehilite_linenos_table(self):
+ if has_pygments:
+ expected = (
+ '<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div>'
+ '</td><td class="code"><div class="codehilite"><pre><span></span><code>plain text\n'
+ '</code></pre></div>\n'
+ '</td></tr></table>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text linenums">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', linenos='table')
+
+ def test_codehilite_linenos_inline(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="lineno">1 </span>plain text\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text linenums">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', linenos='inline')
+
+ def test_codehilite_linenums_true(self):
+ if has_pygments:
+ expected = (
+ '<table class="codehilitetable"><tr><td class="linenos"><div class="linenodiv"><pre>1</pre></div>'
+ '</td><td class="code"><div class="codehilite"><pre><span></span><code>plain text\n'
+ '</code></pre></div>\n'
+ '</td></tr></table>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text linenums">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', linenums=True)
+
+ def test_codehilite_set_cssclass(self):
+ if has_pygments:
+ expected = (
+ '<div class="override"><pre><span></span><code>plain text\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="override"><code class="language-text">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', cssclass='override')
+
+ def test_codehilite_set_css_class(self):
+ if has_pygments:
+ expected = (
+ '<div class="override"><pre><span></span><code>plain text\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="override"><code class="language-text">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', css_class='override')
+
+ def test_codehilite_linenostart(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="lineno">42 </span>plain text\n'
+ '</code></pre></div>'
+ )
+ else:
+ # TODO: Implement linenostart for no-pygments. Will need to check what JS libs look for.
+ expected = (
+ '<pre class="codehilite"><code class="language-text linenums">plain text\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('plain text', expected, lang='text', linenos='inline', linenostart=42)
+
+ def test_codehilite_linenos_hl_lines(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code>'
+ '<span class="lineno">1 </span><span class="hll">line 1\n'
+ '</span><span class="lineno">2 </span>line 2\n'
+ '<span class="lineno">3 </span><span class="hll">line 3\n'
+ '</span></code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text linenums">line 1\n'
+ 'line 2\n'
+ 'line 3\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', hl_lines=[1, 3])
+
+ def test_codehilite_linenos_linenostep(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="lineno"> </span>line 1\n'
+ '<span class="lineno">2 </span>line 2\n'
+ '<span class="lineno"> </span>line 3\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text linenums">line 1\n'
+ 'line 2\n'
+ 'line 3\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', linenostep=2)
+
+ def test_codehilite_linenos_linenospecial(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="lineno">1 </span>line 1\n'
+ '<span class="lineno special">2 </span>line 2\n'
+ '<span class="lineno">3 </span>line 3\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text linenums">line 1\n'
+ 'line 2\n'
+ 'line 3\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('line 1\nline 2\nline 3', expected, lang='text', linenos='inline', linenospecial=2)
+
+ def test_codehilite_startinline(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="k">print</span><span class="p">(</span>'
+ '<span class="s2">"Hello World"</span><span class="p">);</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-php">print("Hello World");\n'
+ '</code></pre>'
+ )
+ self.assertOutputEquals('print("Hello World");', expected, lang='php', startinline=True)
+
+
+class TestCodeHiliteExtension(TestCase):
+ """ Test codehilite extension. """
+
+ maxDiff = None
+
+ def testBasicCodeHilite(self):
+ if has_pygments:
+ # Odd result as no lang given and a single comment is not enough for guessing.
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="err"># A Code Comment</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code># A Code Comment\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ '\t# A Code Comment',
+ expected,
+ extensions=['codehilite']
+ )
+
+ def testLinenumsTrue(self):
+ if has_pygments:
+ expected = (
+ '<table class="codehilitetable"><tr>'
+ '<td class="linenos"><div class="linenodiv"><pre>1</pre></div></td>'
+ '<td class="code"><div class="codehilite"><pre><span></span>'
+ '<code><span class="err"># A Code Comment</span>\n'
+ '</code></pre></div>\n'
+ '</td></tr></table>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="linenums"># A Code Comment\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ '\t# A Code Comment',
+ expected,
+ extensions=[CodeHiliteExtension(linenums=True)]
+ )
+
+ def testLinenumsFalse(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="c1"># A Code Comment</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-python"># A Code Comment\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t#!Python\n'
+ '\t# A Code Comment'
+ ),
+ expected,
+ extensions=[CodeHiliteExtension(linenums=False)]
+ )
+
+ def testLinenumsNone(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="err"># A Code Comment</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code># A Code Comment\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ '\t# A Code Comment',
+ expected,
+ extensions=[CodeHiliteExtension(linenums=None)]
+ )
+
+ def testLinenumsNoneWithShebang(self):
+ if has_pygments:
+ expected = (
+ '<table class="codehilitetable"><tr>'
+ '<td class="linenos"><div class="linenodiv"><pre>1</pre></div></td>'
+ '<td class="code"><div class="codehilite"><pre><span></span>'
+ '<code><span class="c1"># A Code Comment</span>\n'
+ '</code></pre></div>\n'
+ '</td></tr></table>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-python linenums"># A Code Comment\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t#!Python\n'
+ '\t# A Code Comment'
+ ),
+ expected,
+ extensions=[CodeHiliteExtension(linenums=None)]
+ )
+
+ def testLinenumsNoneWithColon(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="c1"># A Code Comment</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-python"># A Code Comment\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t:::Python\n'
+ '\t# A Code Comment'
+ ),
+ expected,
+ extensions=[CodeHiliteExtension(linenums=None)]
+ )
+
+ def testHighlightLinesWithColon(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="hll"><span class="c1">#line 1</span>\n'
+ '</span><span class="c1">#line 2</span>\n'
+ '<span class="c1">#line 3</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-python">#line 1\n'
+ '#line 2\n'
+ '#line 3\n'
+ '</code></pre>'
+ )
+ # Double quotes
+ self.assertMarkdownRenders(
+ (
+ '\t:::Python hl_lines="1"\n'
+ '\t#line 1\n'
+ '\t#line 2\n'
+ '\t#line 3'
+ ),
+ expected,
+ extensions=['codehilite']
+ )
+ # Single quotes
+ self.assertMarkdownRenders(
+ (
+ "\t:::Python hl_lines='1'\n"
+ '\t#line 1\n'
+ '\t#line 2\n'
+ '\t#line 3'
+ ),
+ expected,
+ extensions=['codehilite']
+ )
+
+ def testUsePygmentsFalse(self):
+ self.assertMarkdownRenders(
+ (
+ '\t:::Python\n'
+ '\t# A Code Comment'
+ ),
+ (
+ '<pre class="codehilite"><code class="language-python"># A Code Comment\n'
+ '</code></pre>'
+ ),
+ extensions=[CodeHiliteExtension(use_pygments=False)]
+ )
+
+ def testLangPrefixEmpty(self):
+ self.assertMarkdownRenders(
+ (
+ '\t:::Python\n'
+ '\t# A Code Comment'
+ ),
+ (
+ '<pre class="codehilite"><code class="python"># A Code Comment\n'
+ '</code></pre>'
+ ),
+ extensions=[CodeHiliteExtension(use_pygments=False, lang_prefix='')]
+ )
+
+ def testLangPrefix(self):
+ self.assertMarkdownRenders(
+ (
+ '\t:::Python\n'
+ '\t# A Code Comment'
+ ),
+ (
+ '<pre class="codehilite"><code class="lang-python"># A Code Comment\n'
+ '</code></pre>'
+ ),
+ extensions=[CodeHiliteExtension(use_pygments=False, lang_prefix='lang-')]
+ )
+
+ def testDoubleEscape(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre>'
+ '<span></span>'
+ '<code><span class="p"><</span><span class="nt">span</span><span class="p">></span>'
+ 'This<span class="ni">&amp;</span>That'
+ '<span class="p"></</span><span class="nt">span</span><span class="p">></span>'
+ '\n</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-html">'
+ '<span>This&amp;That</span>\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t:::html\n'
+ '\t<span>This&That</span>'
+ ),
+ expected,
+ extensions=['codehilite']
+ )
+
+ def testEntitiesIntact(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre>'
+ '<span></span>'
+ '<code>< &lt; and > &gt;'
+ '\n</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text">'
+ '< &lt; and > &gt;\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t:::text\n'
+ '\t< < and > >'
+ ),
+ expected,
+ extensions=['codehilite']
+ )
+
+ def testHighlightAmps(self):
+ if has_pygments:
+ expected = (
+ '<div class="codehilite"><pre><span></span><code>&\n'
+ '&amp;\n'
+ '&amp;amp;\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code class="language-text">&\n'
+ '&amp;\n'
+ '&amp;amp;\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ (
+ '\t:::text\n'
+ '\t&\n'
+ '\t&\n'
+ '\t&amp;'
+ ),
+ expected,
+ extensions=['codehilite']
+ )
+
+ def testUnknownOption(self):
+ if has_pygments:
+ # Odd result as no lang given and a single comment is not enough for guessing.
+ expected = (
+ '<div class="codehilite"><pre><span></span><code><span class="err"># A Code Comment</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code># A Code Comment\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ '\t# A Code Comment',
+ expected,
+ extensions=[CodeHiliteExtension(unknown='some value')],
+ )
--- /dev/null
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestDefList(TestCase):
+
+ def test_def_list_with_ol(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+
+ term
+
+ : this is a definition for term. it has
+ multiple lines in the first paragraph.
+
+ 1. first thing
+
+ first thing details in a second paragraph.
+
+ 1. second thing
+
+ second thing details in a second paragraph.
+
+ 1. third thing
+
+ third thing details in a second paragraph.
+ '''
+ ),
+ self.dedent(
+ '''
+ <dl>
+ <dt>term</dt>
+ <dd>
+ <p>this is a definition for term. it has
+ multiple lines in the first paragraph.</p>
+ <ol>
+ <li>
+ <p>first thing</p>
+ <p>first thing details in a second paragraph.</p>
+ </li>
+ <li>
+ <p>second thing</p>
+ <p>second thing details in a second paragraph.</p>
+ </li>
+ <li>
+ <p>third thing</p>
+ <p>third thing details in a second paragraph.</p>
+ </li>
+ </ol>
+ </dd>
+ </dl>
+ '''
+ ),
+ extensions=['def_list']
+ )
+
+ def test_def_list_with_ul(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+
+ term
+
+ : this is a definition for term. it has
+ multiple lines in the first paragraph.
+
+ - first thing
+
+ first thing details in a second paragraph.
+
+ - second thing
+
+ second thing details in a second paragraph.
+
+ - third thing
+
+ third thing details in a second paragraph.
+ '''
+ ),
+ self.dedent(
+ '''
+ <dl>
+ <dt>term</dt>
+ <dd>
+ <p>this is a definition for term. it has
+ multiple lines in the first paragraph.</p>
+ <ul>
+ <li>
+ <p>first thing</p>
+ <p>first thing details in a second paragraph.</p>
+ </li>
+ <li>
+ <p>second thing</p>
+ <p>second thing details in a second paragraph.</p>
+ </li>
+ <li>
+ <p>third thing</p>
+ <p>third thing details in a second paragraph.</p>
+ </li>
+ </ul>
+ </dd>
+ </dl>
+ '''
+ ),
+ extensions=['def_list']
+ )
+
+ def test_def_list_with_nesting(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+
+ term
+
+ : this is a definition for term. it has
+ multiple lines in the first paragraph.
+
+ 1. first thing
+
+ first thing details in a second paragraph.
+
+ - first nested thing
+
+ second nested thing details
+ '''
+ ),
+ self.dedent(
+ '''
+ <dl>
+ <dt>term</dt>
+ <dd>
+ <p>this is a definition for term. it has
+ multiple lines in the first paragraph.</p>
+ <ol>
+ <li>
+ <p>first thing</p>
+ <p>first thing details in a second paragraph.</p>
+ <ul>
+ <li>
+ <p>first nested thing</p>
+ <p>second nested thing details</p>
+ </li>
+ </ul>
+ </li>
+ </ol>
+ </dd>
+ </dl>
+ '''
+ ),
+ extensions=['def_list']
+ )
+
+ def test_def_list_with_nesting_self(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+
+ term
+
+ : this is a definition for term. it has
+ multiple lines in the first paragraph.
+
+ inception
+
+ : this is a definition for term. it has
+ multiple lines in the first paragraph.
+
+ - bullet point
+
+ another paragraph
+ '''
+ ),
+ self.dedent(
+ '''
+ <dl>
+ <dt>term</dt>
+ <dd>
+ <p>this is a definition for term. it has
+ multiple lines in the first paragraph.</p>
+ <dl>
+ <dt>inception</dt>
+ <dd>
+ <p>this is a definition for term. it has
+ multiple lines in the first paragraph.</p>
+ <ul>
+ <li>bullet point</li>
+ </ul>
+ <p>another paragraph</p>
+ </dd>
+ </dl>
+ </dd>
+ </dl>
+ '''
+ ),
+ extensions=['def_list']
+ )
+
+ def test_def_list_unreasonable_nesting(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+
+ turducken
+
+ : this is a definition for term. it has
+ multiple lines in the first paragraph.
+
+ 1. ordered list
+
+ - nested list
+
+ term
+
+ : definition
+
+ - item 1 paragraph 1
+
+ item 1 paragraph 2
+ '''
+ ),
+ self.dedent(
+ '''
+ <dl>
+ <dt>turducken</dt>
+ <dd>
+ <p>this is a definition for term. it has
+ multiple lines in the first paragraph.</p>
+ <ol>
+ <li>
+ <p>ordered list</p>
+ <ul>
+ <li>
+ <p>nested list</p>
+ <dl>
+ <dt>term</dt>
+ <dd>
+ <p>definition</p>
+ <ul>
+ <li>
+ <p>item 1 paragraph 1</p>
+ <p>item 1 paragraph 2</p>
+ </li>
+ </ul>
+ </dd>
+ </dl>
+ </li>
+ </ul>
+ </li>
+ </ol>
+ </dd>
+ </dl>
+ '''
+ ),
+ extensions=['def_list']
+ )
+
+ def test_def_list_nested_admontions(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ term
+
+ : definition
+
+ !!! note "Admontion"
+
+ term
+
+ : defintion
+
+ 1. list
+
+ continue
+ '''
+ ),
+ self.dedent(
+ '''
+ <dl>
+ <dt>term</dt>
+ <dd>
+ <p>definition</p>
+ <div class="admonition note">
+ <p class="admonition-title">Admontion</p>
+ <dl>
+ <dt>term</dt>
+ <dd>
+ <p>defintion</p>
+ <ol>
+ <li>
+ <p>list</p>
+ <p>continue</p>
+ </li>
+ </ol>
+ </dd>
+ </dl>
+ </div>
+ </dd>
+ </dl>
+ '''
+ ),
+ extensions=['def_list', 'admonition']
+ )
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2007-2019 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
"""
from markdown.test_tools import TestCase
+import markdown
class TestFencedCode(TestCase):
- # TODO: Move the rest of the fenced code tests here.
+ def setUp(self):
+ self.has_pygments = True
+ try:
+ import pygments # noqa
+ except ImportError:
+ self.has_pygments = False
+
+ def testBasicFence(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ A paragraph before a fenced code block:
+
+ ```
+ Fenced code block
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <p>A paragraph before a fenced code block:</p>
+ <pre><code>Fenced code block
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testNestedFence(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ````
+
+ ```
+ ````
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code>
+ ```
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedTildes(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ~~~
+ # Arbitrary code
+ ``` # these backticks will not close the block
+ ~~~
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code># Arbitrary code
+ ``` # these backticks will not close the block
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedLanguageNoDot(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` python
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedLanguageWithDot(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` .python
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedCodeWithHighlightLines(self):
+ if self.has_pygments:
+ expected = self.dedent(
+ '''
+ <div class="codehilite"><pre><span></span><code><span class="hll">line 1
+ </span>line 2
+ <span class="hll">line 3
+ </span></code></pre></div>
+ '''
+ )
+ else:
+ expected = self.dedent(
+ '''
+ <pre class="codehilite"><code>line 1
+ line 2
+ line 3
+ </code></pre>
+ '''
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ```hl_lines="1 3"
+ line 1
+ line 2
+ line 3
+ ```
+ '''
+ ),
+ expected,
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
+ 'fenced_code'
+ ]
+ )
+
+ def testFencedLanguageAndHighlightLines(self):
+ if self.has_pygments:
+ expected = (
+ '<div class="python codehilite"><pre><span></span><code>'
+ '<span class="hll"><span class="n">line</span> <span class="mi">1</span>\n'
+ '</span><span class="n">line</span> <span class="mi">2</span>\n'
+ '<span class="hll"><span class="n">line</span> <span class="mi">3</span>\n'
+ '</span></code></pre></div>'
+ )
+ else:
+ expected = self.dedent(
+ '''
+ <pre class="python codehilite"><code class="language-python">line 1
+ line 2
+ line 3
+ </code></pre>
+ '''
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` .python hl_lines="1 3"
+ line 1
+ line 2
+ line 3
+ ```
+ '''
+ ),
+ expected,
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
+ 'fenced_code'
+ ]
+ )
+
+ def testFencedLanguageAndPygmentsDisabled(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` .python
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False),
+ 'fenced_code'
+ ]
+ )
+
+ def testFencedLanguageDoubleEscape(self):
+ if self.has_pygments:
+ expected = (
+ '<div class="html codehilite"><pre><span></span><code>'
+ '<span class="p"><</span><span class="nt">span</span>'
+ '<span class="p">></span>This<span class="ni">&amp;</span>'
+ 'That<span class="p"></</span><span class="nt">span</span>'
+ '<span class="p">></span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="html codehilite"><code class="language-html">'
+ '<span>This&amp;That</span>\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ```html
+ <span>This&That</span>
+ ```
+ '''
+ ),
+ expected,
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(),
+ 'fenced_code'
+ ]
+ )
+
+ def testFencedAmps(self):
+ if self.has_pygments:
+ expected = self.dedent(
+ '''
+ <div class="text codehilite"><pre><span></span><code>&
+ &amp;
+ &amp;amp;
+ </code></pre></div>
+ '''
+ )
+ else:
+ expected = self.dedent(
+ '''
+ <pre class="text codehilite"><code class="language-text">&
+ &amp;
+ &amp;amp;
+ </code></pre>
+ '''
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ```text
+ &
+ &
+ &amp;
+ ```
+ '''
+ ),
+ expected,
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(),
+ 'fenced_code'
+ ]
+ )
def test_fenced_code_in_raw_html(self):
self.assertMarkdownRenders(
),
extensions=['fenced_code']
)
+
+ def testFencedLanguageInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` {.python}
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedMultipleClassesInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` {.python .foo .bar}
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python foo bar"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedIdInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { #foo }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedIdAndLangInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedIdAndLangAndClassInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo .bar }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python bar"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedCodeWithHighlightLinesInAttr(self):
+ if self.has_pygments:
+ expected = self.dedent(
+ '''
+ <div class="codehilite"><pre><span></span><code><span class="hll">line 1
+ </span>line 2
+ <span class="hll">line 3
+ </span></code></pre></div>
+ '''
+ )
+ else:
+ expected = self.dedent(
+ '''
+ <pre class="codehilite"><code>line 1
+ line 2
+ line 3
+ </code></pre>
+ '''
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ```{ hl_lines="1 3" }
+ line 1
+ line 2
+ line 3
+ ```
+ '''
+ ),
+ expected,
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
+ 'fenced_code'
+ ]
+ )
+
+ def testFencedLanguageAndHighlightLinesInAttr(self):
+ if self.has_pygments:
+ expected = (
+ '<div class="python codehilite"><pre><span></span><code>'
+ '<span class="hll"><span class="n">line</span> <span class="mi">1</span>\n'
+ '</span><span class="n">line</span> <span class="mi">2</span>\n'
+ '<span class="hll"><span class="n">line</span> <span class="mi">3</span>\n'
+ '</span></code></pre></div>'
+ )
+ else:
+ expected = self.dedent(
+ '''
+ <pre class="python codehilite"><code class="language-python">line 1
+ line 2
+ line 3
+ </code></pre>
+ '''
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python hl_lines="1 3" }
+ line 1
+ line 2
+ line 3
+ ```
+ '''
+ ),
+ expected,
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
+ 'fenced_code'
+ ]
+ )
+
+ def testFencedLanguageIdInAttrAndPygmentsDisabled(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False),
+ 'fenced_code'
+ ]
+ )
+
+ def testFencedLanguageIdAndPygmentsDisabledInAttr(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo use_pygments=False }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['codehilite', 'fenced_code']
+ )
+
+ def testFencedLanguageIdAndPygmentsDisabledInAttrNoCodehilite(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo use_pygments=False }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedLanguageIdAndPygmentsEnabledInAttrNoCodehilite(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python #foo use_pygments=True }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="foo"><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code']
+ )
+
+ def testFencedLanguageAttrCssclass(self):
+ if self.has_pygments:
+ expected = self.dedent(
+ '''
+ <div class="python pygments"><pre><span></span><code><span class="c1"># Some python code</span>
+ </code></pre></div>
+ '''
+ )
+ else:
+ expected = (
+ '<pre class="python pygments"><code class="language-python"># Some python code\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python css_class='pygments' }
+ # Some python code
+ ```
+ '''
+ ),
+ expected,
+ extensions=['codehilite', 'fenced_code']
+ )
+
+ def testFencedLanguageAttrLinenums(self):
+ if self.has_pygments:
+ expected = (
+ '<table class="python codehilitetable"><tr>'
+ '<td class="linenos"><div class="linenodiv"><pre>1</pre></div></td>'
+ '<td class="code"><div class="python codehilite"><pre><span></span>'
+ '<code><span class="c1"># Some python code</span>\n'
+ '</code></pre></div>\n'
+ '</td></tr></table>'
+ )
+ else:
+ expected = (
+ '<pre class="python codehilite"><code class="language-python linenums"># Some python code\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python linenums=True }
+ # Some python code
+ ```
+ '''
+ ),
+ expected,
+ extensions=['codehilite', 'fenced_code']
+ )
+
+ def testFencedLanguageAttrGuesslang(self):
+ if self.has_pygments:
+ expected = self.dedent(
+ '''
+ <div class="codehilite"><pre><span></span><code># Some python code
+ </code></pre></div>
+ '''
+ )
+ else:
+ expected = (
+ '<pre class="codehilite"><code># Some python code\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { guess_lang=False }
+ # Some python code
+ ```
+ '''
+ ),
+ expected,
+ extensions=['codehilite', 'fenced_code']
+ )
+
+ def testFencedLanguageAttrNoclasses(self):
+ if self.has_pygments:
+ expected = (
+ '<div class="python codehilite" style="background: #f8f8f8">'
+ '<pre style="line-height: 125%"><span></span><code>'
+ '<span style="color: #408080; font-style: italic"># Some python code</span>\n'
+ '</code></pre></div>'
+ )
+ else:
+ expected = (
+ '<pre class="python codehilite"><code class="language-python"># Some python code\n'
+ '</code></pre>'
+ )
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python noclasses=True }
+ # Some python code
+ ```
+ '''
+ ),
+ expected,
+ extensions=['codehilite', 'fenced_code']
+ )
+
+ def testFencedLanguageNoCodehiliteWithAttrList(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python foo=bar }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python" foo="bar"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code', 'attr_list']
+ )
+
+ def testFencedLanguagePygmentsDisabledInAttrNoCodehiliteWithAttrList(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python foo=bar use_pygments=False }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python" foo="bar"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code', 'attr_list']
+ )
+
+ def testFencedLanguagePygmentsEnabledInAttrNoCodehiliteWithAttrList(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { .python foo=bar use_pygments=True }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="language-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code', 'attr_list']
+ )
+
+ def testFencedLanguageNoPrefix(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` python
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='')]
+ )
+
+ def testFencedLanguageAltPrefix(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` python
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre><code class="lang-python"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='lang-')]
+ )
class TestFootnotes(TestCase):
+ default_kwargs = {'extensions': ['footnotes']}
+ maxDiff = None
+
+ def test_basic_footnote(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ paragraph[^1]
+
+ [^1]: A Footnote
+ """
+ ),
+ '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>A Footnote <a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">↩</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
+
+ def test_multiple_footnotes(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ foo[^1]
+
+ bar[^2]
+
+ [^1]: Footnote 1
+ [^2]: Footnote 2
+ """
+ ),
+ '<p>foo<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '<p>bar<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>Footnote 1 <a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">↩</a></p>\n'
+ '</li>\n'
+ '<li id="fn:2">\n'
+ '<p>Footnote 2 <a class="footnote-backref" href="#fnref:2"'
+ ' title="Jump back to footnote 2 in the text">↩</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
+
+ def test_multiple_footnotes_multiline(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ foo[^1]
+
+ bar[^2]
+
+ [^1]: Footnote 1
+ line 2
+ [^2]: Footnote 2
+ """
+ ),
+ '<p>foo<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '<p>bar<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>Footnote 1\nline 2 <a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">↩</a></p>\n'
+ '</li>\n'
+ '<li id="fn:2">\n'
+ '<p>Footnote 2 <a class="footnote-backref" href="#fnref:2"'
+ ' title="Jump back to footnote 2 in the text">↩</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
+
+ def test_footnote_multi_line(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ paragraph[^1]
+ [^1]: A Footnote
+ line 2
+ """
+ ),
+ '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>A Footnote\nline 2 <a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">↩</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
+
+ def test_footnote_multi_line_lazy_indent(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ paragraph[^1]
+ [^1]: A Footnote
+ line 2
+ """
+ ),
+ '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>A Footnote\nline 2 <a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">↩</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
+
+ def test_footnote_multi_line_complex(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ paragraph[^1]
+
+ [^1]:
+
+ A Footnote
+ line 2
+
+ * list item
+
+ > blockquote
+ """
+ ),
+ '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>A Footnote\nline 2</p>\n'
+ '<ul>\n<li>list item</li>\n</ul>\n'
+ '<blockquote>\n<p>blockquote</p>\n</blockquote>\n'
+ '<p><a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">↩</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
+
+ def test_footnote_multple_complex(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ foo[^1]
+
+ bar[^2]
+
+ [^1]:
+
+ A Footnote
+ line 2
+
+ * list item
+
+ > blockquote
+
+ [^2]: Second footnote
+
+ paragraph 2
+ """
+ ),
+ '<p>foo<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '<p>bar<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>A Footnote\nline 2</p>\n'
+ '<ul>\n<li>list item</li>\n</ul>\n'
+ '<blockquote>\n<p>blockquote</p>\n</blockquote>\n'
+ '<p><a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">↩</a></p>\n'
+ '</li>\n'
+ '<li id="fn:2">\n'
+ '<p>Second footnote</p>\n'
+ '<p>paragraph 2 <a class="footnote-backref" href="#fnref:2"'
+ ' title="Jump back to footnote 2 in the text">↩</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
+
+ def test_footnote_multple_complex_no_blank_line_between(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ foo[^1]
+
+ bar[^2]
+
+ [^1]:
+
+ A Footnote
+ line 2
+
+ * list item
+
+ > blockquote
+ [^2]: Second footnote
+
+ paragraph 2
+ """
+ ),
+ '<p>foo<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '<p>bar<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>A Footnote\nline 2</p>\n'
+ '<ul>\n<li>list item</li>\n</ul>\n'
+ '<blockquote>\n<p>blockquote</p>\n</blockquote>\n'
+ '<p><a class="footnote-backref" href="#fnref:1"'
+ ' title="Jump back to footnote 1 in the text">↩</a></p>\n'
+ '</li>\n'
+ '<li id="fn:2">\n'
+ '<p>Second footnote</p>\n'
+ '<p>paragraph 2 <a class="footnote-backref" href="#fnref:2"'
+ ' title="Jump back to footnote 2 in the text">↩</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
+
def test_backlink_text(self):
"""Test backlink configuration."""
'</li>\n'
'</ol>\n'
'</div>',
- extensions=['footnotes'],
extension_configs={'footnotes': {'BACKLINK_TEXT': 'back'}}
)
'</li>\n'
'</ol>\n'
'</div>',
- extensions=['footnotes'],
extension_configs={'footnotes': {'SEPARATOR': '-'}}
)
'<p>This is text <strong>bold<em>italic bold</em></strong> with more text</p>',
extensions=['legacy_em']
)
+
+ def test_complex_multple_underscore_type(self):
+
+ self.assertMarkdownRenders(
+ 'traced ___along___ bla __blocked__ if other ___or___',
+ '<p>traced <strong><em>along</em></strong> bla <strong>blocked</strong> if other <strong><em>or</em></strong></p>' # noqa: E501
+ )
+
+ def test_complex_multple_underscore_type_variant2(self):
+
+ self.assertMarkdownRenders(
+ 'on the __1-4 row__ of the AP Combat Table ___and___ receive',
+ '<p>on the <strong>1-4 row</strong> of the AP Combat Table <strong><em>and</em></strong> receive</p>'
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from unittest import TestSuite
+from markdown.test_tools import TestCase
+from ..blocks.test_html_blocks import TestHTMLBlocks
+
+
+class TestDefaultwMdInHTML(TestHTMLBlocks):
+ """ Ensure the md_in_html extension does not break the default behavior. """
+
+ default_kwargs = {'extensions': ['md_in_html']}
+
+
+class TestMdInHTML(TestCase):
+
+ default_kwargs = {'extensions': ['md_in_html']}
+
+ def test_md1_paragraph(self):
+ self.assertMarkdownRenders(
+ '<p markdown="1">*foo*</p>',
+ '<p><em>foo</em></p>'
+ )
+
+ def test_md1_p_linebreaks(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p markdown="1">
+ *foo*
+ </p>
+ """
+ ),
+ self.dedent(
+ """
+ <p>
+ <em>foo</em>
+ </p>
+ """
+ )
+ )
+
+ def test_md1_p_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p markdown="1">
+
+ *foo*
+
+ </p>
+ """
+ ),
+ self.dedent(
+ """
+ <p>
+
+ <em>foo</em>
+
+ </p>
+ """
+ )
+ )
+
+ def test_md1_div(self):
+ self.assertMarkdownRenders(
+ '<div markdown="1">*foo*</div>',
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_div_linebreaks(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ *foo*
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_div_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+
+ *foo*
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_div_multi(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+
+ *foo*
+
+ __bar__
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ <p><strong>bar</strong></p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_div_nested(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+
+ <div markdown="1">
+ *foo*
+ </div>
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <div>
+ <p><em>foo</em></p>
+ </div>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_div_multi_nest(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+
+ <div markdown="1">
+ <p markdown="1">*foo*</p>
+ </div>
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <div>
+ <p><em>foo</em></p>
+ </div>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_mix(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ A _Markdown_ paragraph before a raw child.
+
+ <p markdown="1">A *raw* child.</p>
+
+ A _Markdown_ tail to the raw child.
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A <em>Markdown</em> paragraph before a raw child.</p>
+ <p>A <em>raw</em> child.</p>
+ <p>A <em>Markdown</em> tail to the raw child.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_deep_mix(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+
+ A _Markdown_ paragraph before a raw child.
+
+ A second Markdown paragraph
+ with two lines.
+
+ <div markdown="1">
+
+ A *raw* child.
+
+ <p markdown="1">*foo*</p>
+
+ Raw child tail.
+
+ </div>
+
+ A _Markdown_ tail to the raw child.
+
+ A second tail item
+ with two lines.
+
+ <p markdown="1">More raw.</p>
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A <em>Markdown</em> paragraph before a raw child.</p>
+ <p>A second Markdown paragraph
+ with two lines.</p>
+ <div>
+ <p>A <em>raw</em> child.</p>
+ <p><em>foo</em></p>
+ <p>Raw child tail.</p>
+ </div>
+ <p>A <em>Markdown</em> tail to the raw child.</p>
+ <p>A second tail item
+ with two lines.</p>
+ <p>More raw.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_div_raw_inline(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+
+ <em>foo</em>
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ </div>
+ """
+ )
+ )
+
+ def test_no_md1_paragraph(self):
+ self.assertMarkdownRenders(
+ '<p>*foo*</p>',
+ '<p>*foo*</p>'
+ )
+
+ def test_no_md1_nest(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ A _Markdown_ paragraph before a raw child.
+
+ <p>A *raw* child.</p>
+
+ A _Markdown_ tail to the raw child.
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A <em>Markdown</em> paragraph before a raw child.</p>
+ <p>A *raw* child.</p>
+ <p>A <em>Markdown</em> tail to the raw child.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_nested_empty(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ A _Markdown_ paragraph before a raw empty tag.
+
+ <img src="image.png" alt="An image" />
+
+ A _Markdown_ tail to the raw empty tag.
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A <em>Markdown</em> paragraph before a raw empty tag.</p>
+ <p><img src="image.png" alt="An image" /></p>
+ <p>A <em>Markdown</em> tail to the raw empty tag.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_nested_empty_block(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ A _Markdown_ paragraph before a raw empty tag.
+
+ <hr />
+
+ A _Markdown_ tail to the raw empty tag.
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A <em>Markdown</em> paragraph before a raw empty tag.</p>
+ <hr />
+ <p>A <em>Markdown</em> tail to the raw empty tag.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_md_span_paragraph(self):
+ self.assertMarkdownRenders(
+ '<p markdown="span">*foo*</p>',
+ '<p><em>foo</em></p>'
+ )
+
+ def test_md_block_paragraph(self):
+ self.assertMarkdownRenders(
+ '<p markdown="block">*foo*</p>',
+ self.dedent(
+ """
+ <p>
+ <p><em>foo</em></p>
+ </p>
+ """
+ )
+ )
+
+ def test_md_span_div(self):
+ self.assertMarkdownRenders(
+ '<div markdown="span">*foo*</div>',
+ '<div><em>foo</em></div>'
+ )
+
+ def test_md_block_div(self):
+ self.assertMarkdownRenders(
+ '<div markdown="block">*foo*</div>',
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ </div>
+ """
+ )
+ )
+
+ def test_md_span_nested_in_block(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="block">
+ <div markdown="span">*foo*</div>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <div><em>foo</em></div>
+ </div>
+ """
+ )
+ )
+
+ def test_md_block_nested_in_span(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="span">
+ <div markdown="block">*foo*</div>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <div><em>foo</em></div>
+ </div>
+ """
+ )
+ )
+
+ def test_md_block_after_span_nested_in_block(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="block">
+ <div markdown="span">*foo*</div>
+ <div markdown="block">*bar*</div>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <div><em>foo</em></div>
+ <div>
+ <p><em>bar</em></p>
+ </div>
+ </div>
+ """
+ )
+ )
+
+ def test_nomd_nested_in_md1(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ *foo*
+ <div>
+ *foo*
+ <p>*bar*</p>
+ *baz*
+ </div>
+ *bar*
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ <div>
+ *foo*
+ <p>*bar*</p>
+ *baz*
+ </div>
+ <p><em>bar</em></p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_nested_in_nomd(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div>
+ <div markdown="1">*foo*</div>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <div markdown="1">*foo*</div>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_single_quotes(self):
+ self.assertMarkdownRenders(
+ "<p markdown='1'>*foo*</p>",
+ '<p><em>foo</em></p>'
+ )
+
+ def test_md1_no_quotes(self):
+ self.assertMarkdownRenders(
+ '<p markdown=1>*foo*</p>',
+ '<p><em>foo</em></p>'
+ )
+
+ def test_md_no_value(self):
+ self.assertMarkdownRenders(
+ '<p markdown>*foo*</p>',
+ '<p><em>foo</em></p>'
+ )
+
+ def test_md1_preserve_attrs(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1" id="parent">
+
+ <div markdown="1" class="foo">
+ <p markdown="1" class="bar baz">*foo*</p>
+ </div>
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div id="parent">
+ <div class="foo">
+ <p class="bar baz"><em>foo</em></p>
+ </div>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_unclosed_div(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+
+ _foo_
+
+ <div class="unclosed">
+
+ _bar_
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ <div class="unclosed">
+
+ _bar_
+
+ </div>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_orphan_endtag(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+
+ _foo_
+
+ </p>
+
+ _bar_
+
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em></p>
+ </p>
+ <p><em>bar</em></p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_unclosed_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <p markdown="1">_foo_
+ <p markdown="1">_bar_
+ """
+ ),
+ self.dedent(
+ """
+ <p><em>foo</em>
+ </p>
+ <p><em>bar</em>
+
+ </p>
+ """
+ )
+ )
+
+ def test_md1_nested_unclosed_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ <p markdown="1">_foo_
+ <p markdown="1">_bar_
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p><em>foo</em>
+ </p>
+ <p><em>bar</em>
+ </p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_nested_comment(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ A *Markdown* paragraph.
+ <!-- foobar -->
+ A *Markdown* paragraph.
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <p>A <em>Markdown</em> paragraph.</p>
+ <!-- foobar -->
+ <p>A <em>Markdown</em> paragraph.</p>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_nested_link_ref(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ [link]: http://example.com
+ <div markdown="1">
+ [link][link]
+ </div>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <div>
+ <p><a href="http://example.com">link</a></p>
+ </div>
+ </div>
+ """
+ )
+ )
+
+ def test_md1_nested_abbr_ref(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ *[abbr]: Abbreviation
+ <div markdown="1">
+ abbr
+ </div>
+ </div>
+ """
+ ),
+ self.dedent(
+ """
+ <div>
+ <div>
+ <p><abbr title="Abbreviation">abbr</abbr></p>
+ </div>
+ </div>
+ """
+ ),
+ extensions=['md_in_html', 'abbr']
+ )
+
+ def test_md1_nested_footnote_ref(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ <div markdown="1">
+ [^1]: The footnote.
+ <div markdown="1">
+ Paragraph with a footnote.[^1]
+ </div>
+ </div>
+ """
+ ),
+ '<div>\n'
+ '<div>\n'
+ '<p>Paragraph with a footnote.<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup></p>\n'
+ '</div>\n'
+ '</div>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>The footnote. '
+ '<a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">↩</a>'
+ '</p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>',
+ extensions=['md_in_html', 'footnotes']
+ )
+
+
+def load_tests(loader, tests, pattern):
+ ''' Ensure TestHTMLBlocks doesn't get run twice by excluding it here. '''
+ suite = TestSuite()
+ for test_class in [TestDefaultwMdInHTML, TestMdInHTML]:
+ tests = loader.loadTestsFromTestCase(test_class)
+ suite.addTests(tests)
+ return suite
'</h1>', # noqa
extensions=[TocExtension(permalink=True, permalink_title="")]
)
+
+ def testPermalinkWithUnicodeInID(self):
+ from markdown.extensions.toc import slugify_unicode
+ self.assertMarkdownRenders(
+ '# Unicode ヘッダー',
+ '<h1 id="unicode-ヘッター">' # noqa
+ 'Unicode ヘッダー' # noqa
+ '<a class="headerlink" href="#unicode-ヘッター" title="Permanent link">¶</a>' # noqa
+ '</h1>', # noqa
+ extensions=[TocExtension(permalink=True, slugify=slugify_unicode)]
+ )
+
+ def testPermalinkWithUnicodeTitle(self):
+ from markdown.extensions.toc import slugify_unicode
+ self.assertMarkdownRenders(
+ '# Unicode ヘッダー',
+ '<h1 id="unicode-ヘッター">' # noqa
+ 'Unicode ヘッダー' # noqa
+ '<a class="headerlink" href="#unicode-ヘッター" title="パーマリンク">¶</a>' # noqa
+ '</h1>', # noqa
+ extensions=[TocExtension(permalink=True, permalink_title="パーマリンク", slugify=slugify_unicode)]
+ )
'This text is **bold *italic* *italic* bold**',
'<p>This text is <strong>bold <em>italic</em> <em>italic</em> bold</strong></p>'
)
+
+ def test_complex_multple_emphasis_type(self):
+
+ self.assertMarkdownRenders(
+ 'traced ***along*** bla **blocked** if other ***or***',
+ '<p>traced <strong><em>along</em></strong> bla <strong>blocked</strong> if other <strong><em>or</em></strong></p>' # noqa: E501
+ )
+
+ def test_complex_multple_emphasis_type_variant2(self):
+
+ self.assertMarkdownRenders(
+ 'on the **1-4 row** of the AP Combat Table ***and*** receive',
+ '<p>on the <strong>1-4 row</strong> of the AP Combat Table <strong><em>and</em></strong> receive</p>'
+ )
"""""",
"""<p><img alt="Image" src="http://humane man.jpg" /></p>"""
)
+
+ def test_short_ref(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ![ref]
+
+ [ref]: ./image.jpg
+ """
+ ),
+ '<p><img alt="ref" src="./image.jpg" /></p>'
+ )
+
+ def test_short_ref_in_link(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [![img ref]](http://example.com/)
+
+ [img ref]: ./image.jpg
+ """
+ ),
+ '<p><a href="http://example.com/"><img alt="img ref" src="./image.jpg" /></a></p>'
+ )
from markdown.test_tools import TestCase
-class TestAdvancedLinks(TestCase):
+class TestInlineLinks(TestCase):
def test_nested_square_brackets(self):
self.assertMarkdownRenders(
'<p><a href="http://example.com/?a=1&b=2">title</a></p>'
)
+
+class TestReferenceLinks(TestCase):
+
+ def test_ref_link(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: http://example.com
+ """
+ ),
+ """<p><a href="http://example.com">Text</a></p>"""
+ )
+
+ def test_ref_link_angle_brackets(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: <http://example.com>
+ """
+ ),
+ """<p><a href="http://example.com">Text</a></p>"""
+ )
+
+ def test_ref_link_no_space(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]:http://example.com
+ """
+ ),
+ """<p><a href="http://example.com">Text</a></p>"""
+ )
+
+ def test_ref_link_angle_brackets_no_space(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]:<http://example.com>
+ """
+ ),
+ """<p><a href="http://example.com">Text</a></p>"""
+ )
+
+ def test_ref_link_angle_brackets_title(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: <http://example.com> "title"
+ """
+ ),
+ """<p><a href="http://example.com" title="title">Text</a></p>"""
+ )
+
+ def test_ref_link_title(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: http://example.com "title"
+ """
+ ),
+ """<p><a href="http://example.com" title="title">Text</a></p>"""
+ )
+
+ def test_ref_link_angle_brackets_title_no_space(self):
+ # TODO: Maybe reevaluate this?
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: <http://example.com>"title"
+ """
+ ),
+ """<p><a href="http://example.com>"title"">Text</a></p>"""
+ )
+
+ def test_ref_link_title_no_space(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: http://example.com"title"
+ """
+ ),
+ """<p><a href="http://example.com"title"">Text</a></p>"""
+ )
+
+ def test_ref_link_single_quoted_title(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: http://example.com 'title'
+ """
+ ),
+ """<p><a href="http://example.com" title="title">Text</a></p>"""
+ )
+
+ def test_ref_link_title_nested_quote(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: http://example.com "title'"
+ """
+ ),
+ """<p><a href="http://example.com" title="title'">Text</a></p>"""
+ )
+
+ def test_ref_link_single_quoted_title_nested_quote(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: http://example.com 'title"'
+ """
+ ),
+ """<p><a href="http://example.com" title="title"">Text</a></p>"""
+ )
+
+ def test_ref_link_override(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]: http://example.com 'ignore'
+ [Text]: https://example.com 'override'
+ """
+ ),
+ """<p><a href="https://example.com" title="override">Text</a></p>"""
+ )
+
+ def test_ref_link_title_no_blank_lines(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+ [Text]: http://example.com "title"
+ [Text]
+ """
+ ),
+ self.dedent(
+ """
+ <p><a href="http://example.com" title="title">Text</a></p>
+ <p><a href="http://example.com" title="title">Text</a></p>
+ """
+ )
+ )
+
+ def test_ref_link_multi_line(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ [Text]
+
+ [Text]:
+ http://example.com
+ "title"
+ """
+ ),
+ """<p><a href="http://example.com" title="title">Text</a></p>"""
+ )
+
def test_reference_newlines(self):
"""Test reference id whitespace cleanup."""
[tox]
-envlist = py35, py36, py37, py38, pypy3, flake8, checkspelling, pep517check, checklinks
+envlist = py36, py37, py38, py39, pypy3, pygments, flake8, checkspelling, pep517check, checklinks
isolated_build = True
-min_verison = 1.9
[testenv]
extras = testing
deps = pytidylib
-commands = coverage run --source=markdown -m unittest discover {toxinidir}/tests
- coverage report --show-missing
+commands =
+ coverage run --source=markdown -m unittest discover {toxinidir}/tests
+ coverage xml
+ coverage report --show-missing
+
+[testenv:pygments]
+# Run tests with pygments installed (override deps only).
+deps =
+ pytidylib
+ pygments==2.5.2
[testenv:flake8]
deps = flake8