Giter VIP home page Giter VIP logo

mdit-py-plugins's Introduction

mdit-py-plugins's People

Contributors

andersk avatar chrisjsewell avatar cjolowicz avatar dependabot[bot] avatar distractedmosfet avatar eric-wieser avatar hukkin avatar hukkinj1 avatar jpmckinney avatar kyleking avatar llimllib avatar pre-commit-ci[bot] avatar rowanc1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mdit-py-plugins's Issues

dollarmath_plugin `renderer` parameter does not work correctly in `0.3.4`

Describe the bug

The dollarmath_plugin's renderer parameter does not work after upgrading to 0.3.4. Instead of using the given renderer function to parse the LaTeX, it just returns the name of the renderer function. Here's a simple example for repro:

In <=0.3.3

from markdown_it import MarkdownIt
md = MarkdownIt().use(dollarmath_plugin, renderer=lambda x,y:x).enable("table")

md.render("# Let's learn about $x$")

would produce:

<h1>Let\'s learn about <span class="math inline">x</span></h1>

In 0.3.4

it produces

<h1>Let's learn about <span class="math inline"><function <lambda> at 0x7fd79cb4d940></span></h1>

Reproduce the bug

See above

List your environment

Python 3.9
Windows, Linux

More context

We use the wonderful markdown-it-py and mdit-py-plugins packages in the open-source Gradio library, where we use it to render LaTeX in machine learning demos. We'd love to continue being able to do so, so hoping this bug gets fixed. Thanks!

Admonition title syntax is not consistent with other implementation

Describe the bug

context

The admonition syntax is based on Python Markdown:

!!! danger "Don't try this at home"
    ...

expectation

I would expect the quotes to be absent from the output.

bug

With mdit-py-plugins, the quotes are present in the output:

image

Unexpected output generated by mdit-py-plugins (spurious quotes):

<div class="admonition danger"> <p class="admonition-title">"Don't try this at home"</p> <p>...</p> </div>

Moreover, the additional CSS classes are not supported either.

Changing the behavior might cause compatiblity issues with markdown-it-admon however.

problem

This is a problem for people migrating from Python Markdown.

Reproduce the bug

Create markdown file with:

!!! danger "Don't try this at home"
    ...

Create script

from markdown_it import MarkdownIt
from mdit_py_plugins.admon import admon_plugin

md_opts = {
    "html": True,
}

markdown_it_render = MarkdownIt("commonmark", md_opts).use(admon_plugin)

source = open("/dev/stdin", "rt").read()
res = markdown_it_render.render(source)
print(res)

Run:

python3 test.py < test.md

Output:

<div class="admonition danger">
<p class="admonition-title">&quot;Don't try this at home&quot;</p>
<p>...</p>
</div>

List your environment

  • Python 3.11.4
  • Debian Linux
  • markdown-it-py==3.0.0
  • mdit-py-plugins==0.4.0

Add fancy list support

Description / Summary

I'd like to see a fancy list plugin. I'm using this for formatting laws and they have deeply nested, ordered lists with different numbering styles for each level.

Value / benefit

Deeply nested, ordered lists would be clearer.

Implementation details

There is a markdown-it plugin here: https://github.com/Moxio/markdown-it-fancy-lists

Tasks to complete

No response

Support spaces in labels

Describe the enhancement you'd like

It would be useful if it were possible to use spaces in labels, like so:

(my label)=
# My header

Ref for [](my label).

In executablebooks/meta#467 (comment) we discovered that, while reStructuredText supports spaces in labels, like .. _my label:, the MyST parser does not.

Benefit

This would be useful for people who are coming from the rST / Sphinx ecosystem, since this is already supported in rST. However it's probably not a huge number of people using labels this way because most seem to use - or _ for this kind of thing.

Implementation

The fix for this would probably somewhere around here:

def target(state: StateBlock, startLine: int, endLine: int, silent: bool):

Fix the required version for markdow-it-py

Describe the bug

During the packaging of the last version of mdit-py-plugins in Debian, some tests failed:

=========================== short test summary info ============================
FAILED tests/test_admon.py::test_all[306-Indented by 4 spaces, DISABLE-CODEBLOCKS-    ??? note\n        content\n-<div class="admonition note is-collapsible collapsible-closed">\n<p class="admonition-title">Note</p>\n<p>content</p>\n</div>\n]
FAILED tests/test_amsmath.py::test_fixtures[237-Indented by 4 spaces, DISABLE-CODEBLOCKS-    \\begin{equation}\n    a = 1\n    \\end{equation}\n-<div class="math amsmath">\n\\begin{equation}\n    a = 1\n    \\end{equation}\n</div>\n]
FAILED tests/test_attrs.py::test_attrs[254-Indented by 4 spaces, DISABLE-CODEBLOCKS-    {#a .a b=c}\n    # head\n-<h1 id="a" b="c" class="a">head</h1>\n]
FAILED tests/test_colon_fence.py::test_fixtures[438-Indented by 4 spaces, DISABLE-CODEBLOCKS-    :::name\n    foo\n    :::\n-<pre><code class="block-name" >foo\n</code></pre>\n]
FAILED tests/test_container.py::test_all[301-Indented by 4 spaces, DISABLE-CODEBLOCKS-    ::: name\n    content\n    :::\n-<div class="name">\n<p>content</p>\n</div>\n]
FAILED tests/test_deflist.py::test_all[261-Indented by 4 spaces, DISABLE-CODEBLOCKS-    Term 1\n\n    : Definition 1\n-<dl>\n<dt>Term 1</dt>\n<dd>Definition 1</dd>\n</dl>\n]
FAILED tests/test_dollarmath.py::test_dollarmath_fixtures[575-Indented by 4 spaces, DISABLE-CODEBLOCKS-    $$a$$\n-<div class="math block">\na\n</div>\n]
FAILED tests/test_field_list.py::test_all[378-Indented_by_4_spaces,_DISABLE-CODEBLOCKS]
FAILED tests/test_footnote.py::test_all[360-Indented by 4 spaces, DISABLE-CODEBLOCKS-    [^1]\n\n    [^1]: footnote\n-<p><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>footnote <a href="#fnref1" class="footnote-backref">\u21a9\ufe0e</a></p>\n</li>\n</ol>\n</section>\n]
FAILED tests/test_myst_block.py::test_all[168-Indented by 4 spaces, DISABLE-CODEBLOCKS-    +++\n\n    % abc\n\n    (a)=\n-<hr class="myst-block">\n<!-- abc --><div class="myst-target"><a href="#a">(a)=</a></div>\n]
======================== 10 failed, 400 passed in 0.45s ========================

All of those failed with the form:

    @pytest.mark.parametrize("line,title,input,expected", read_fixture_file(FIXTURE_PATH))
    def test_all(line, title, input, expected):
        md = MarkdownIt("commonmark").use(myst_block_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       assert '<p>+++</p>\n...\n<p>(a)=</p>' == '<hr class="m...a)=</a></div>'
E         - <hr class="myst-block">
E         - <!-- abc --><div class="myst-target"><a href="#a">(a)=</a></div>
E         + <p>+++</p>
E         + <p>% abc</p>
E         + <p>(a)=</p>

I figured out that updating the version of markdown-it-py package fix the error.

Looking #89, that use markdow-it-py 2.2.0, and I used version 2.1.0, seems that the dependencies variable in pyproject.toml can be change to avoid confusion:

dependencies = ["markdown-it-py>=3.0.0,<4.0.0"]

Reproduce the bug

Run tests with versions of markdown-it-py minor than 3.0.0

List your environment

No response

Package on pypi.org does not include tests

Describe the bug

I'm new to python and it's packaging ecosystem ... I was expecting to find all the tests in the package that's distributed from pypi.org ... is this a reasonable expectation?

Reproduce the bug

Download package from pypi.org & extract.

List your environment

x

Improvements to the tasklists plugin

Context

I would suggest two improvements to the tasklists plugin:

  • Don't add disabled="disabled" to the checkboxes. This makes it so you can't click them. But if you are going to add a list of check boxes to a page, it would be useful if people can actually click them. Since this might not be desired in all cases, a configuration option might be useful, or some syntax to allow or disallow it.

  • Add style="list-style: none; styling to the ul. This prevents the bullet point from appearing. This is how it works on GitHub, for instance

  • Item 1

  • Item 2

Proposal

No response

Tasks and updates

No response

Typo in Github "about" text

Github "About" text is Repository containg plugins for markdown-it-py. Should change containg to containing

Usage example

Context

The usage example

from markdown_it import MarkdownIt
from mdit_py_plugins import plugin1, plugin2
md = MarkdownIt().use(plugin1, keyword=value).use(plugin2, keyword=value)
html_string = md.render("some *Markdown*")

is, I feel, a little misleading, since the plugins can't actually be imported directly from the top-level mdit_py_plugins module currently.

Proposal

Maybe a real example would be better?

Tasks and updates

No response

tasklist: NestedToken have no attr when is used without any text content

Describe the bug
When we parse the markdown text of tasklist type, the tokens provide no information if the markdown is of type tasklist, even if we use the tasklist plugin

To Reproduce

from markdown_it import MarkdownIt
from markdown_it.token import NestedTokens, Token, nest_tokens
from mdit_py_plugins.tasklists import tasklists_plugin

def parse_markdown(md_text):
    mdi = MarkdownIt("commonmark").enable("table").use(tasklists_plugin)
    tokens = mdi.parse(text)
    tokens = nest_tokens(tokens)

    return tokens

>>> text = "- [ ] "
>>> parse_markdown(text)
[NestedTokens(opening=Token(type='bullet_list_open', tag='ul', nesting=1, attrs=None, map=[0, 1], level=0, children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), closing=Toke
n(type='bullet_list_close', tag='ul', nesting=-1, attrs=None, map=None, level=0, children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), children=[NestedTokens(opening=Token(t
ype='list_item_open', tag='li', nesting=1, attrs=None, map=[0, 1], level=1, children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), closing=Token(type='list_item_close', tag='
li', nesting=-1, attrs=None, map=None, level=1, children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), children=[NestedTokens(opening=Token(type='paragraph_open', tag='p', ne
sting=1, attrs=None, map=[0, 1], level=2, children=None, content='', markup='', info='', meta={}, block=True, hidden=True), closing=Token(type='paragraph_close', tag='p', nesting=-1, attrs=None, map=None
, level=2, children=None, content='', markup='', info='', meta={}, block=True, hidden=True), children=[Token(type='inline', tag='', nesting=0, attrs=None, map=[0, 1], level=3, children=[Token(type='text'
, tag='', nesting=0, attrs=None, map=None, level=0, children=None, content='[ ]', markup='', info='', meta={}, block=False, hidden=False)], content='[ ]', markup='', info='', meta={}, block=True, hidden=
False)])])])]
>>> 
>>> # the below text will have attrs in bullet_list_open
... 
>>> text = "- [ ] Tasklist 1"
>>> parse_markdown(text)
[NestedTokens(opening=Token(type='bullet_list_open', tag='ul', nesting=1, attrs=[['class', 'contains-task-list']], map=[0, 1], level=0, children=None, content='', markup='-', info='', meta={}, block=True
, hidden=False), closing=Token(type='bullet_list_close', tag='ul', nesting=-1, attrs=None, map=None, level=0, children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), children=
[NestedTokens(opening=Token(type='list_item_open', tag='li', nesting=1, attrs=[['class', 'task-list-item']], map=[0, 1], level=1, children=None, content='', markup='-', info='', meta={}, block=True, hidd
en=False), closing=Token(type='list_item_close', tag='li', nesting=-1, attrs=None, map=None, level=1, children=None, content='', markup='-', info='', meta={}, block=True, hidden=False), children=[NestedT
okens(opening=Token(type='paragraph_open', tag='p', nesting=1, attrs=None, map=[0, 1], level=2, children=None, content='', markup='', info='', meta={}, block=True, hidden=True), closing=Token(type='parag
raph_close', tag='p', nesting=-1, attrs=None, map=None, level=2, children=None, content='', markup='', info='', meta={}, block=True, hidden=True), children=[Token(type='inline', tag='', nesting=0, attrs=
None, map=[0, 1], level=3, children=[Token(type='html_inline', tag='', nesting=0, attrs=None, map=None, level=0, children=None, content='<input class="task-list-item-checkbox" disabled="disabled" type="c
heckbox">', markup='', info='', meta={}, block=False, hidden=False), Token(type='text', tag='', nesting=0, attrs=None, map=None, level=0, children=None, content=' Tasklist 1', markup='', info='', meta={}
, block=False, hidden=False)], content=' Tasklist 1', markup='', info='', meta={}, block=True, hidden=False)])])])]

Expected behavior
Expected behaviour should be that a text - [ ] should also have ['class', 'contains-task-list'] in NestedToken.opening

Environment

  • Python Version - 3.6
  • Package versions or output of jupyter-book --version:
  • Operating System- Ubuntu

converted formula lost its delimiters

Describe the bug

context
I used markdown_it to convert Markdown to html, but the converted formula lost its delimiters, which caused my mathjax cannot render the formula,

Code for conversion :

from markdown_it import MarkdownIt
from mdit_py_plugins.front_matter import front_matter_plugin
from mdit_py_plugins.footnote import footnote_plugin
from mdit_py_plugins.anchors import anchors_plugin
from mdit_py_plugins.texmath import  texmath_plugin
from mdit_py_plugins.dollarmath import  dollarmath_plugin
from mdit_py_plugins.amsmath import amsmath_plugin
from markdown_it.common import  utils

import html
md = (
    # MarkdownIt('gfm-like' ,{
    MarkdownIt('commonmark' ,{
        'breaks':True,
        'html':True
        })


    # .use(texmath_plugin, delimiters="dollars")
    .use(dollarmath_plugin)
    # .use(amsmath_plugin)
	.use(front_matter_plugin)
    .use(footnote_plugin)
    .use(anchors_plugin, permalink=True, max_level=4)
    
    .enable('table')
)
s4=r'''a rational number like $\begin{matrix}
L & \  = \{ x \mid x \in \mathbb{Q},x \leq 0\} \cup \left\{ x \mid x \in \mathbb{Q},x > 0,x^{2} < 2 \right\} \\
U & \  = \mathbb{Q} - L = \left\{ x \mid x \in \mathbb{Q},x > 0,x^{2} > 2 \right\} \\
\end{matrix}$ .
  2.  A fact between two Dedekind cuts(the density of $Q$ in $R$): For any pair of real numbers $\alpha$ and $\beta$, where $\alpha > \beta$, there can always be found a real, and even in particular a rational, number $r$ which lies between them, i.e. $\alpha > r > \beta$ (and, consequently, an infinite set of such rational numbers).
'''
html_text = md.render(s4)
print(html_text)

The demo with the result at https://jsbin.com/ronasug/edit?html,output

expectation
delimiters around formula.

bug
converted formula lost its delimiters, the bug is also confirmed at https://groups.google.com/g/mathjax-users/c/aL3aqfp8DzU/m/KZoomKKvBwAJ

... but it looks like it might be an old one, as it seems to be producing jsMath-style delimiters (...) rather than MathJax-style ones. JsMath was MathJax's predecessor, active from 2004 to 2008, and while MathJax v2 includes an extension that would parse jsMath delimiters, that is not available in version 3. There is an example showing how to look for MathJax v2 <script> tags that could be modified to look for jsMath-style tags instead, if you are proficient enough with javascript.

Reproduce the bug

see the above section

List your environment

No response

Consider disallowing backslashes in MyST targets

Describe the problem/need and solution

Context
In mdit-py-plugins < 0.3 it backslashes were not allowed in MyST targets. Now they are.

Problem / Idea
This is now a valid MyST target:

(myst_target\)=

I think it would be more intuitive and in line with CommonMark if the backslash escape worked, i.e. prevented the target being parsed successfully.

Solution
Disallow backslashes in MyST targets.

Benefit
Consistency with backslashes in CommonMark.

Guide for implementation

No response

Tasks and updates

No response

0.4.0: pytest is failing

Describe the bug

Looks like with new version pytest is failing in few units.

Reproduce the bug

I'm packaging your module as an rpm package so I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.

  • python3 -sBm build -w --no-isolation
  • because I'm calling build with --no-isolation I'm using during all processes only locally installed modules
  • install .whl file in </install/prefix> using 'installer` module
  • run pytest with $PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>
  • build is performed in env which is cut off from access to the public network (pytest is executed with -m "not network")

Here is pytest output:

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-mdit-py-plugins-0.4.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-mdit-py-plugins-0.4.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra -m 'not network'
==================================================================================== test session starts ====================================================================================
platform linux -- Python 3.8.16, pytest-7.3.1, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/mdit-py-plugins-0.4.0
plugins: datadir-1.4.1, regressions-2.4.2
collected 410 items

tests/test_admon.py .....................F...                                                                                                                                         [  6%]
tests/test_amsmath.py ...................F                                                                                                                                            [ 10%]
tests/test_anchors.py ........                                                                                                                                                        [ 12%]
tests/test_attrs.py ...............................F                                                                                                                                  [ 20%]
tests/test_colon_fence.py ................................F.                                                                                                                          [ 29%]
tests/test_container.py ......................F                                                                                                                                       [ 34%]
tests/test_deflist.py ...............F                                                                                                                                                [ 38%]
tests/test_dollarmath.py ...............................................................F                                                                                             [ 54%]
tests/test_field_list.py ................F                                                                                                                                            [ 58%]
tests/test_footnote.py ......................F                                                                                                                                        [ 63%]
tests/test_front_matter.py .........                                                                                                                                                  [ 66%]
tests/test_myst_block.py ..............F..                                                                                                                                            [ 70%]
tests/test_myst_role.py ...............                                                                                                                                               [ 73%]
tests/test_substitution.py ..........                                                                                                                                                 [ 76%]
tests/test_tasklists.py .......                                                                                                                                                       [ 78%]
tests/test_texmath.py .......................................................................................                                                                         [ 99%]
tests/test_wordcount.py ...                                                                                                                                                           [100%]

========================================================================================= FAILURES ==========================================================================================
_ test_all[306-Indented by 4 spaces, DISABLE-CODEBLOCKS-    ??? note\n        content\n-<div class="admonition note is-collapsible collapsible-closed">\n<p class="admonition-title">Note</p>\n<p>content</p>\n</div>\n] _

line = 306, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    ??? note\n        content\n'
expected = '<div class="admonition note is-collapsible collapsible-closed">\n<p class="admonition-title">Note</p>\n<p>content</p>\n</div>\n'

    @pytest.mark.parametrize(
        "line,title,input,expected",
        read_fixture_file(FIXTURE_PATH.joinpath("fixtures", "admon.md")),
    )
    def test_all(line, title, input, expected):
        md = MarkdownIt("commonmark").use(admon_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       assert '<p>??? note\ncontent</p>' == '<div class="...t</p>\n</div>'
E         + <p>??? note
E         - <div class="admonition note is-collapsible collapsible-closed">
E         - <p class="admonition-title">Note</p>
E         - <p>content</p>
E         ? ---           -
E         + content</p>
E         - </div>

tests/test_admon.py:24: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>??? note
content</p>

_ test_fixtures[237-Indented by 4 spaces, DISABLE-CODEBLOCKS-    \\begin{equation}\n    a = 1\n    \\end{equation}\n-<div class="math amsmath">\n\\begin{equation}\n    a = 1\n    \\end{equation}\n</div>\n] _

line = 237, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    \\begin{equation}\n    a = 1\n    \\end{equation}\n'
expected = '<div class="math amsmath">\n\\begin{equation}\n    a = 1\n    \\end{equation}\n</div>\n'

    @pytest.mark.parametrize(
        "line,title,input,expected",
        read_fixture_file(FIXTURE_PATH.joinpath("fixtures", "amsmath.md")),
    )
    def test_fixtures(line, title, input, expected):
        md = MarkdownIt("commonmark").use(amsmath_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       assert '<p>\\begin{e...equation}</p>' == '<div class="...tion}\n</div>'
E         - <div class="math amsmath">
E         - \begin{equation}
E         + <p>\begin{equation}
E         ? +++
E         -     a = 1
E         ? ----
E         + a = 1...
E
E         ...Full output truncated (5 lines hidden), use '-vv' to show

tests/test_amsmath.py:58: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>\begin{equation}
a = 1
\end{equation}</p>

_______________________________ test_attrs[254-Indented by 4 spaces, DISABLE-CODEBLOCKS-    {#a .a b=c}\n    # head\n-<h1 id="a" b="c" class="a">head</h1>\n] _______________________________

line = 254, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    {#a .a b=c}\n    # head\n', expected = '<h1 id="a" b="c" class="a">head</h1>\n'

    @pytest.mark.parametrize(
        "line,title,input,expected", read_fixture_file(FIXTURE_PATH / "attrs.md")
    )
    def test_attrs(line, title, input, expected):
        md = MarkdownIt("commonmark").use(attrs_plugin, spans=True).use(attrs_block_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       assert '<p>{#a .a b=c}\n# head</p>' == '<h1 id="a" b..."a">head</h1>'
E         - <h1 id="a" b="c" class="a">head</h1>
E         + <p>{#a .a b=c}
E         + # head</p>

tests/test_attrs.py:22: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>{#a .a b=c}
# head</p>

______________________ test_fixtures[438-Indented by 4 spaces, DISABLE-CODEBLOCKS-    :::name\n    foo\n    :::\n-<pre><code class="block-name" >foo\n</code></pre>\n] ______________________

line = 438, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    :::name\n    foo\n    :::\n', expected = '<pre><code class="block-name" >foo\n</code></pre>\n'

    @pytest.mark.parametrize("line,title,input,expected", read_fixture_file(FIXTURE_PATH))
    def test_fixtures(line, title, input, expected):
        md = MarkdownIt("commonmark").use(colon_fence_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        try:
>           assert text.rstrip() == expected.rstrip()
E           assert '<p>:::name\nfoo\n:::</p>' == '<pre><code c...</code></pre>'
E             - <pre><code class="block-name" >foo
E             - </code></pre>
E             + <p>:::name
E             + foo
E             + :::</p>

tests/test_colon_fence.py:21: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>:::name
foo
:::</p>

_________________________ test_all[301-Indented by 4 spaces, DISABLE-CODEBLOCKS-    ::: name\n    content\n    :::\n-<div class="name">\n<p>content</p>\n</div>\n] __________________________

line = 301, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    ::: name\n    content\n    :::\n', expected = '<div class="name">\n<p>content</p>\n</div>\n'

    @pytest.mark.parametrize("line,title,input,expected", read_fixture_file(FIXTURE_PATH))
    def test_all(line, title, input, expected):
        md = MarkdownIt("commonmark").use(container_plugin, "name")
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       assert '<p>::: name\...tent\n:::</p>' == '<div class="...t</p>\n</div>'
E         - <div class="name">
E         - <p>content</p>
E         - </div>
E         + <p>::: name
E         + content
E         + :::</p>

tests/test_container.py:50: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>::: name
content
:::</p>

______________________ test_all[261-Indented by 4 spaces, DISABLE-CODEBLOCKS-    Term 1\n\n    : Definition 1\n-<dl>\n<dt>Term 1</dt>\n<dd>Definition 1</dd>\n</dl>\n] ______________________

line = 261, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    Term 1\n\n    : Definition 1\n', expected = '<dl>\n<dt>Term 1</dt>\n<dd>Definition 1</dd>\n</dl>\n'

    @pytest.mark.parametrize("line,title,input,expected", read_fixture_file(FIXTURE_PATH))
    def test_all(line, title, input, expected):
        md = MarkdownIt("commonmark").use(deflist_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       AssertionError: assert '<p>Term 1</p...inition 1</p>' == '<dl>\n<dt>Te...1</dd>\n</dl>'
E         - <dl>
E         - <dt>Term 1</dt>
E         ?  ^^         ^^
E         + <p>Term 1</p>
E         ?  ^         ^
E         - <dd>Definition 1</dd>
E         ?  ^^               ^^ -...
E
E         ...Full output truncated (3 lines hidden), use '-vv' to show

tests/test_deflist.py:39: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>Term 1</p>
<p>: Definition 1</p>

_________________________________ test_dollarmath_fixtures[575-Indented by 4 spaces, DISABLE-CODEBLOCKS-    $$a$$\n-<div class="math block">\na\n</div>\n] __________________________________

line = 575, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    $$a$$\n', expected = '<div class="math block">\na\n</div>\n'

    @pytest.mark.parametrize(
        "line,title,input,expected",
        read_fixture_file(FIXTURE_PATH.joinpath("dollar_math.md")),
    )
    def test_dollarmath_fixtures(line, title, input, expected):
        md = MarkdownIt("commonmark").use(
            dollarmath_plugin,
            allow_space=False,
            allow_digits=False,
            double_inline=True,
            allow_blank_lines=False,
        )
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options.xhtmlOut = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       assert '<p><div clas...">a</div></p>' == '<div class="...">\na\n</div>'
E         + <p><div class="math inline">a</div></p>
E         - <div class="math block">
E         - a
E         - </div>

tests/test_dollarmath.py:106: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p><div class="math inline">a</div></p>

__________________________________________________________________ test_all[378-Indented_by_4_spaces,_DISABLE-CODEBLOCKS] ___________________________________________________________________

line = 378, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    :name: text\n        indented\n'
expected = '<dl class="field-list">\n<dt>name</dt>\n<dd>\n<p>text\nindented</p>\n</dd>\n</dl>\n'

    @pytest.mark.parametrize(
        "line,title,input,expected",
        fixtures,
        ids=[f"{f[0]}-{f[1].replace(' ', '_')}" for f in fixtures],
    )
    def test_all(line, title, input, expected):
        md = MarkdownIt("commonmark").use(fieldlist_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       assert '<p>:name: text\nindented</p>' == '<dl class="f...n</dd>\n</dl>'
E         + <p>:name: text
E         - <dl class="field-list">
E         - <dt>name</dt>
E         - <dd>
E         - <p>text
E         - indented</p>
E         ?             -...
E
E         ...Full output truncated (3 lines hidden), use '-vv' to show

tests/test_field_list.py:41: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>:name: text
indented</p>

_ test_all[360-Indented by 4 spaces, DISABLE-CODEBLOCKS-    [^1]\n\n    [^1]: footnote\n-<p><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>footnote <a href="#fnref1" class="footnote-backref">\u21a9\ufe0e</a></p>\n</li>\n</ol>\n</section>\n] _

line = 360, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    [^1]\n\n    [^1]: footnote\n'
expected = '<p><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<hr class="footnotes-sep">\n<section class...1" class="footnote-item"><p>footnote <a href="#fnref1" class="footnote-backref">↩︎</a></p>\n</li>\n</ol>\n</section>\n'

    @pytest.mark.parametrize("line,title,input,expected", read_fixture_file(FIXTURE_PATH))
    def test_all(line, title, input, expected):
        md = MarkdownIt("commonmark").use(footnote_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip().replace("↩︎", "<-").replace(
            "↩", "<-"
        ) == expected.rstrip().replace("↩︎", "<-").replace("↩", "<-")
E       assert '<p>[^1]</p>\... footnote</p>' == '<p><sup clas...>\n</section>'
E         + <p>[^1]</p>
E         + <p>[^1]: footnote</p>
E         - <p><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>
E         - <hr class="footnotes-sep">
E         - <section class="footnotes">
E         - <ol class="footnotes-list">
E         - <li id="fn1" class="footnote-item"><p>footnote <a href="#fnref1" class="footnote-backref"><-</a></p>...
E
E         ...Full output truncated (3 lines hidden), use '-vv' to show

tests/test_footnote.py:459: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>[^1]</p>
<p>[^1]: footnote</p>

___ test_all[168-Indented by 4 spaces, DISABLE-CODEBLOCKS-    +++\n\n    % abc\n\n    (a)=\n-<hr class="myst-block">\n<!-- abc --><div class="myst-target"><a href="#a">(a)=</a></div>\n] ___

line = 168, title = 'Indented by 4 spaces, DISABLE-CODEBLOCKS', input = '    +++\n\n    % abc\n\n    (a)=\n'
expected = '<hr class="myst-block">\n<!-- abc --><div class="myst-target"><a href="#a">(a)=</a></div>\n'

    @pytest.mark.parametrize("line,title,input,expected", read_fixture_file(FIXTURE_PATH))
    def test_all(line, title, input, expected):
        md = MarkdownIt("commonmark").use(myst_block_plugin)
        if "DISABLE-CODEBLOCKS" in title:
            md.disable("code")
        md.options["xhtmlOut"] = False
        text = md.render(input)
        print(text)
>       assert text.rstrip() == expected.rstrip()
E       assert '<p>+++</p>\n...\n<p>(a)=</p>' == '<hr class="m...a)=</a></div>'
E         - <hr class="myst-block">
E         - <!-- abc --><div class="myst-target"><a href="#a">(a)=</a></div>
E         + <p>+++</p>
E         + <p>% abc</p>
E         + <p>(a)=</p>

tests/test_myst_block.py:21: AssertionError
----------------------------------------------------------------------------------- Captured stdout call ------------------------------------------------------------------------------------
<p>+++</p>
<p>% abc</p>
<p>(a)=</p>

================================================================================== short test summary info ==================================================================================
FAILED tests/test_admon.py::test_all[306-Indented by 4 spaces, DISABLE-CODEBLOCKS-    ??? note\n        content\n-<div class="admonition note is-collapsible collapsible-closed">\n<p class="admonition-title">Note</p>\n<p>content</p>\n</div>\n] - assert '<p>??? note\ncontent</p>' == '<div class="...t</p>\n</div>'
FAILED tests/test_amsmath.py::test_fixtures[237-Indented by 4 spaces, DISABLE-CODEBLOCKS-    \\begin{equation}\n    a = 1\n    \\end{equation}\n-<div class="math amsmath">\n\\begin{equation}\n    a = 1\n    \\end{equation}\n</div>\n] - assert '<p>\\begin{e...equation}</p>' == '<div class="...tion}\n</div>'
FAILED tests/test_attrs.py::test_attrs[254-Indented by 4 spaces, DISABLE-CODEBLOCKS-    {#a .a b=c}\n    # head\n-<h1 id="a" b="c" class="a">head</h1>\n] - assert '<p>{#a .a b=c}\n# head</p>' == '<h1 id="a" b..."a">head</h1>'
FAILED tests/test_colon_fence.py::test_fixtures[438-Indented by 4 spaces, DISABLE-CODEBLOCKS-    :::name\n    foo\n    :::\n-<pre><code class="block-name" >foo\n</code></pre>\n] - assert '<p>:::name\nfoo\n:::</p>' == '<pre><code c...</code></pre>'
FAILED tests/test_container.py::test_all[301-Indented by 4 spaces, DISABLE-CODEBLOCKS-    ::: name\n    content\n    :::\n-<div class="name">\n<p>content</p>\n</div>\n] - assert '<p>::: name\...tent\n:::</p>' == '<div class="...t</p>\n</div>'
FAILED tests/test_deflist.py::test_all[261-Indented by 4 spaces, DISABLE-CODEBLOCKS-    Term 1\n\n    : Definition 1\n-<dl>\n<dt>Term 1</dt>\n<dd>Definition 1</dd>\n</dl>\n] - AssertionError: assert '<p>Term 1</p...inition 1</p>' == '<dl>\n<dt>Te...1</dd>\n</dl>'
FAILED tests/test_dollarmath.py::test_dollarmath_fixtures[575-Indented by 4 spaces, DISABLE-CODEBLOCKS-    $$a$$\n-<div class="math block">\na\n</div>\n] - assert '<p><div clas...">a</div></p>' == '<div class="...">\na\n</div>'
FAILED tests/test_field_list.py::test_all[378-Indented_by_4_spaces,_DISABLE-CODEBLOCKS] - assert '<p>:name: text\nindented</p>' == '<dl class="f...n</dd>\n</dl>'
FAILED tests/test_footnote.py::test_all[360-Indented by 4 spaces, DISABLE-CODEBLOCKS-    [^1]\n\n    [^1]: footnote\n-<p><sup class="footnote-ref"><a href="#fn1" id="fnref1">[1]</a></sup></p>\n<hr class="footnotes-sep">\n<section class="footnotes">\n<ol class="footnotes-list">\n<li id="fn1" class="footnote-item"><p>footnote <a href="#fnref1" class="footnote-backref">\u21a9\ufe0e</a></p>\n</li>\n</ol>\n</section>\n] - assert '<p>[^1]</p>\... footnote</p>' == '<p><sup clas...>\n</section>'
FAILED tests/test_myst_block.py::test_all[168-Indented by 4 spaces, DISABLE-CODEBLOCKS-    +++\n\n    % abc\n\n    (a)=\n-<hr class="myst-block">\n<!-- abc --><div class="myst-target"><a href="#a">(a)=</a></div>\n] - assert '<p>+++</p>\n...\n<p>(a)=</p>' == '<hr class="m...a)=</a></div>'
============================================================================== 10 failed, 400 passed in 1.22s ===============================================================================

List your environment

Here is list of installed modules in build env

Package                       Version
----------------------------- -------
alabaster                     0.7.13
asttokens                     2.2.1
Babel                         2.12.1
backcall                      0.2.0
build                         0.10.0
charset-normalizer            3.1.0
decorator                     5.1.1
distro                        1.8.0
docutils                      0.19
exceptiongroup                1.1.1
executing                     1.2.0
gpg                           1.20.0
idna                          3.4
imagesize                     1.4.1
importlib-metadata            6.6.0
iniconfig                     2.0.0
installer                     0.7.0
ipython                       8.12.0
jedi                          0.18.2
Jinja2                        3.1.2
libcomps                      0.1.19
markdown-it-py                2.2.0
MarkupSafe                    2.1.2
matplotlib-inline             0.1.6
mdit-py-plugins               0.3.5
mdurl                         0.1.2
myst-parser                   1.0.0
packaging                     23.1
parso                         0.8.3
pexpect                       4.8.0
pickleshare                   0.7.5
pluggy                        1.0.0
prompt-toolkit                3.0.38
ptyprocess                    0.7.0
pure-eval                     0.2.2
Pygments                      2.15.1
pyproject_hooks               1.0.0
pytest                        7.3.1
pytest-datadir                1.4.1
pytest-regressions            2.4.2
python-dateutil               2.8.2
pytz                          2023.2
PyYAML                        6.0
requests                      2.30.0
setuptools                    67.7.2
six                           1.16.0
snowballstemmer               2.2.0
Sphinx                        6.2.1
sphinxcontrib-applehelp       1.0.4
sphinxcontrib-devhelp         1.0.2
sphinxcontrib-htmlhelp        2.0.0
sphinxcontrib-jsmath          1.0.1
sphinxcontrib-qthelp          1.0.3
sphinxcontrib-serializinghtml 1.1.5
stack-data                    0.6.2
tomli                         2.0.1
traitlets                     5.9.0
typing_extensions             4.5.0
urllib3                       1.26.15
wcwidth                       0.2.6
wheel                         0.40.0
zipp                          3.15.0

Update dollarmath to be inline with JS version

I've just created markdown-it-dollarmath, and in particular here: executablebooks/markdown-it-dollarmath@e3eb244, I added the extra token math_inline_double and renamed math_block_eqno to math_block_label.
Also, there is the labelNormalizer option and the render hook (and similarly with amsmath).

Note, this will also require a change in myst-parser, so should be followed by a minor (breaking) version release

Should link to the JS plugin demonstration pages as well.

Improve handing of tabs

Describe the bug

According to hukkin/mdformat-gfm#23 (comment) there is an issue with the parser regarding how tabs are handled.

I do not know how exactly, but they produce a side effect in the mdformat-gfm plugin.

Reproduce the bug

Use mdformat with the mdformat-gfm plugin.

Some brackets are escaped even when using mdformat-gfm in cases where the closing bracket is followed with a tab.

Example file:
https://gist.githubusercontent.com/mdeweerd/177a260cc0deb83566ce85847e0bab71/raw/ba5cf9671a1a9d10ec03ddea9d00b6a2a2cf5365/Test%2520Markdown%2520with%2520tabs%2520and%2520tickboxes.md

List your environment

mdformat 0.7.13 (mdformat_gfm: 0.3.5, mdformat_tables: 0.4.1, mdformat_toc:
0.3.0)

myst_role misparses trailing \

Describe the bug

context

myst_role misparses inlines that begin with a role occurences and end with \ because its checks for leading \ unintentionally wraps around the end of srcCharCode and checks the last char code instead.

expectation

>>> md.render("{foo}`ar`\\")
'<p><code class="myst role">{foo}[ar]</code>\\</p>\n'

bug

>>> md.render("{foo}`ar`\\")
'<p>{foo}<code>ar</code>\\</p>\n'

problem
This is a pretty clear misparse that causes rendering errors. admittedly it's not very likely to be hit in the grand scheme of things, but we've hit it nevertheless.

Reproduce the bug

render any string that contains only a myst_role followed by \, with no leading or trailing whitespace.

List your environment

  • markdown_it: 2.1.0
  • mdit_py_plugins: 0.3.3
  • python: 3.10.9
  • OS: nixos unstable

Add github / gitlab parsing of issues and pull requests

Context

Github and gitlab flavored markdown have their own parsing tools to detection links to issues with a simple #<number> or !<number> for gitlab's MR.

Example here : #103 #14 automatically add links to the corresponding issue and pull requests

Would it be interesting to have a dedicated plugin for myst ?

Many projects do have a CHANGELOG.md file, which uses this syntaxe and it would be nice to be able to convert it to a sphinx page with myst. See a changelog example here : https://github.com/github-tools/github-release-notes/blob/master/CHANGELOG.md

Also, note that there exists a project that tries to recreate the #{number} feel for changelog with sphinx, but it works directive that are then not parsed by github/gitlab : https://github.com/sloria/sphinx-issues .

In a sense, we want the other way around, to transform #{number} patterns into proper directives 😃

Proposal

Provide a plugin that you can parameter so that {character}{number} are substituted to links to the corresponding issue/PR of you own project {website}/{user}/{project}/{task}/{number}

for e.g. this project, the parameters would be :

website = "https://github.com/"
user = "exectuablebooks"
project = "mdit-py-plugins"
issue_character = "#"
issue_task = "issues"

For a project in gitlab the parameters would be :

website = "https://gitlab.com/"
user = "exectuablebooks"
project = "mdit-py-plugins"
issue_character = "#"
issue_task = "issues"
pull_request_character="!"
pull_request_task = "merge_requests"

Note that for github, there is a confusion between patterns for issues and pull requests, but that's ok since github automatically redirect to the right url. For example #105 automatically redirects to #105

Tasks and updates

No response

anchors_plugin callback

I want a list of the title, unique slug-id pairs for my generated anchors so I can create a navbar linking to the content. In MarkdownIt for JS a callback exists with this data:

"The callback option is a function that will be called at the end of rendering with the token and an info object. The info object has title and slug properties with the token content and the slug used for the identifier."

How can this be done with the current plugin?

I thought about a custom parsing function for heading_open token, but how to get inline after?

I considering passing a custom slug_func to anchors_plugin, but it doesn't have access to the slugs param so it can not retrieve the unique_slug id for the heading.

I tried parsing first, then rendering, but the plugin maintains state and so the unique_slug ids in the token stream don't match what is rendered.

Future of Admon Plugin

Context

Since the admon plugin was initially ported to mdit-py-plugins, there have been requests for improvements (supporting collapsible MKDocs-style admonitions, MKDocs content tabs, etc.)

In response, I've refactored the logic to make it more extensible and published new versions of mdformat-mkdocs (for the mkdocs-related code) and mdformat-admon with the extensible version of the python-markdown !!! syntax (https://github.com/KyleKing/mdformat-admon/blob/d48ca09eb3160dbfbb2c8219319cba7e63ff8e4a/mdformat_admon/mdit_plugins/_python_markdown_admon.py)

Proposal

Because there is currently duplicate implementations of the admon logic (here and in mdformat-admon), we should try to reduce the duplication. I think there are a couple of options:

  1. Remove the admon plugin from mdit-py-plugins and point users to mdformat-admon if they want that functionality (we could have mdit-py-plugins raise an exception if someone tries to import with a better error or just remove entirely to reduce the maintenance burden
  2. Move a subset of the logic from mdformat-admon into mdit-py-plugins (the extensible factories and updated plugin) to replace what is currently here

Option 2 has the benefit of being easier to support because any issues with admonitions will be on a repo I maintain and all bug issues will go directly to me. Option 1 would be better because of backward compatibility and the usage of the the factory code without the dependency on mdformat.

What is your preference moving forward?

Tasks and updates

No response

Admonition: generate <details> with ???

Context

The admonition module supports ??? and ???+ for collapsible admonitions. This is inspired by mkdocs-material.

In this case, mdit-py-plugins generates:

<div class="admonition note is-collapsible collapsible-closed">
  <div class="admonition-title"></div>
</div>
<div class="admonition note is-collapsible collapsible-open">
    <div class="admonition-title"></div>
</div>

On the other hand, mkdocs-material generates:

<details class="note">
  <summary></summary>
</detail>
<details class="note" open="open">
    <summary></summary>
</detail>

Proposal

It should be possible to generate <details> and <summary> using ??? and ???+. An option should be provided to opt-in the new behavior (or bring back the current one).

Tasks and updates

  • Add option for implementing ??? with details and summary

footnote with blank first line

Describe the bug

The following markdown is a valid footnote:

[^lorem]

[^lorem]:
    ipsum

GitHub screenshot:

sc

However, mdit parses it as if it was invalid becase the first line is empty.

(See executablebooks/mdformat-footnote#6).

Reproduce the bug

See above.

List your environment

  • No jupyter.

  • python --version
    
    Python 3.10.5
    
  • Ubuntu 22.10

The HTML I output doesn't have CSS. What's the reason?

Describe the bug

context
my code
from markdown_it import MarkdownIt
from mdit_py_plugins.front_matter import front_matter_plugin
from mdit_py_plugins.footnote import footnote_plugin
from mdit_py_plugins.texmath import texmath_plugin
md = (
MarkdownIt()
.use(front_matter_plugin)
.use(footnote_plugin)
.use(texmath_plugin)
.enable('image')
.enable('table')
)

all_the_text = open('markdown.md').read()
tokens = md.parse(all_the_text)
html_text = md.render(all_the_text)

expectation
The code is highlighted and the mathematical formula is displayed normally

I checked the output HTML, no head tag, no CSS, no JS file. Why? What do I need to do?

Reproduce the bug

1.code is like this:
from markdown_it import MarkdownIt
from mdit_py_plugins.front_matter import front_matter_plugin
from mdit_py_plugins.footnote import footnote_plugin
from mdit_py_plugins.texmath import texmath_plugin
md = (
MarkdownIt()
.use(front_matter_plugin)
.use(footnote_plugin)
.use(texmath_plugin)
.enable('image')
.enable('table')
)

all_the_text = open('markdown.txt').read()
tokens = md.parse(all_the_text)
html_text = md.render(all_the_text)

List your environment

No response

Add more official plugins

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.