51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Python-Markdown 自定义拓展

现在大部分跟代码有关的编辑器和网站基本都是支持 markdown 语法的,我比较喜欢的 markdown 内容显示的网站是 vitepress 的网站,vitepress 支持的语法比较全,而且有很多个性化的渲染语法,是原生的 markdown 不具备的。

本文分享一下利用 markdown 自带的拓展类进行自定义渲染语法。

自定义行渲染规则 {#自定义行渲染规则}

行渲染规则就是指单行内容的渲染,一般是比较短的渲染,比如最常见的加粗语法: **加粗** 就是行渲染规则。

而我发现 Python的 markdown 库居然不支持"删除文本"的规则,当然也可能有什么参数我没有开启所以才没支持。不过没关系,我可以自定义这个规则。

可以看一下官方给的自定义的代码:https://python-markdown.github.io/extensions/api/#example_3

官方文档这里的例子就是在实现一个"删除文本"的规则,可以直接拿来用:

from markdown.inlinepatterns import InlineProcessor
from markdown.extensions import Extension
import xml.etree.ElementTree as etree

class DelInlineProcessor(InlineProcessor):
def handleMatch(self, m, data):
el = etree.Element('del')
el.text = m.group(1)
return el, m.start(0), m.end(0)

class` `DelExtension(Extension):`
`def` `extendMarkdown(self,` `md):`
`DEL_PATTERN` `=` `r'--(.*?)--'`  `# like --del--`
`md.inlinePatterns.register(DelInlineProcessor(DEL_PATTERN,` `md),` `'del',` `175)`
`

文档里面说了,这个 InlineProcessor 类的第一个调用函数 handleMatch 的参数有两个,第一个参数 m 就是利用正则匹配到的内容,如正则 r'--(.*?)--' 匹配的内容。

将匹配的内容放到自定义的标签里面的方式是使用 etree.Element('del'),这个就可以给匹配内容添加标签,实现一个简单的行内容渲染规则。

然后使用自定义的规则:

from utils.markdown_ext import (
    DelExtension,
    IconExtension
)
def` `make_markdown():`
`md` `=` `markdown.Markdown(extensions=[`
`'markdown.extensions.extra',`
`'markdown_checklist.extension',`
`CodeHiliteExtension(pygments_formatter=CustomHtmlFormatter),`
`TocExtension(slugify=slugify),`
`DelExtension(),`  `# 自定义规则`
`])`
`return` `md`
`

删除文本规则 {#删除文本规则}

这是我自定义的删除文本规则(在例子里面稍微改了一下):

# utils/markdown_ext.py
class DelInlineProcessor(InlineProcessor):
    def handleMatch(self, m, data):
        el = etree.Element('del')
        el.text = m.group(1)
        return el, m.start(0), m.end(0)

class DelExtension(Extension):
    """
    删除文本
    匹配:~~删除~~
    输出:<del>删除</del>
    """


    <span class="k">def</span> <span class="nf">extendMarkdown</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">md</span><span class="p">):</span>
        <span class="n">DEL_PATTERN</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">'~~(.*?)~~'</span>  <span class="c1"># like ~~del~~</span>
        <span class="n">md</span><span class="o">.</span><span class="n">inlinePatterns</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">DelInlineProcessor</span><span class="p">(</span><span class="n">DEL_PATTERN</span><span class="p">,</span> <span class="n">md</span><span class="p">),</span> <span class="s1">'del'</span><span class="p">,</span> <span class="mi">175</span><span class="p">)</span>




图标规则 {#图标规则}

为了方便在文本里面使用图标,我按照删除文本的规则定义了一个渲染图标的规则:

class IconInlineProcessor(InlineProcessor):
    def handleMatch(self, m, data):
        text = m.group(1)
        el = etree.Element('i')
        el.set('class', 'fa fa-{}'.format(text.replace('icon:', '')))
        return el, m.start(0), m.end(0)

class IconExtension(Extension):
    """
    渲染图标
    匹配:icon:exclamation-triangle
    输出:<i class="fa fa-exclamation-triangle"></i>
    """


    <span class="k">def</span> <span class="nf">extendMarkdown</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">md</span><span class="p">):</span>
        <span class="n">DEL_PATTERN</span> <span class="o">=</span> <span class="sa">r</span><span class="s1">'(icon:[a-z-]+)'</span>
        <span class="n">md</span><span class="o">.</span><span class="n">inlinePatterns</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">IconInlineProcessor</span><span class="p">(</span><span class="n">DEL_PATTERN</span><span class="p">,</span> <span class="n">md</span><span class="p">),</span> <span class="s1">'icon'</span><span class="p">,</span> <span class="mi">180</span><span class="p">)</span>




这个规则很简单,比如输入icon:home就可以被渲染成 <i class="fa fa-home"></i> 这种图标的标签。

自定义块渲染规则 {#自定义块渲染规则}

块渲染规则是指将一个段落渲染成 html 的段落,比如代码块就是块渲染。

我这里想要实现一个块渲染效果,最后可以生成 bootstrap 里面的 alert 效果,这里可以看一下目标的效果 html 的代码:

<div class="alert alert-primary" role="alert">
  A simple primary alert---check it out!
</div>
<div class="alert alert-secondary" role="alert">
  A simple secondary alert---check it out!
</div>
<div class="alert alert-success" role="alert">
  A simple success alert---check it out!
</div>
<div class="alert alert-danger" role="alert">
  A simple danger alert---check it out!
</div>
<div class="alert alert-warning" role="alert">
  A simple warning alert---check it out!
</div>
<div class="alert alert-info" role="alert">
  A simple info alert---check it out!
</div>
<div class="alert alert-light" role="alert">
  A simple light alert---check it out!
</div>
<div class="alert alert-dark" role="alert">
  A simple dark alert---check it out!
</div>

这些都是 bootstrap 里面的效果,其实就是在 div 标签里加了一个 class 然后添加了一个固定的 role="alert"

我希望输入的规则是这样的:

:::info
文本内容
:::

也就是这个块需要使用 :::开头和结尾,并且开头里面必须填写一个级别。

官方示例 {#官方示例}

先看一下官方的实例

def test_block_processor():
    class BoxBlockProcessor(BlockProcessor):
        RE_FENCE_START = r'^ *!{3,} *\n' # start line, e.g., `   !!!! `
        RE_FENCE_END = r'\n *!{3,}\s*$'  # last non-blank line, e.g, '!!!\n  \n\n'

        <span class="k">def</span> <span class="nf">test</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
            <span class="k">return</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_START</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>

        <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">blocks</span><span class="p">):</span>
            <span class="n">original_block</span> <span class="o">=</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
            <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_START</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>

            <span class="c1"># Find block with ending fence</span>
            <span class="k">for</span> <span class="n">block_num</span><span class="p">,</span> <span class="n">block</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">blocks</span><span class="p">):</span>
                <span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_END</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
                    <span class="c1"># remove fence</span>
                    <span class="n">blocks</span><span class="p">[</span><span class="n">block_num</span><span class="p">]</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_END</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
                    <span class="c1"># render fenced area inside a new div</span>
                    <span class="n">e</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">SubElement</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="s1">'div'</span><span class="p">)</span>
                    <span class="n">e</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'style'</span><span class="p">,</span> <span class="s1">'display: inline-block; border: 1px solid red;'</span><span class="p">)</span>
                    <span class="bp">self</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">parseBlocks</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">block_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
                    <span class="c1"># remove used blocks</span>
                    <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">block_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
                        <span class="n">blocks</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
                    <span class="k">return</span> <span class="kc">True</span>  <span class="c1"># or could have had no return statement</span>
            <span class="c1"># No closing marker!  Restore and do nothing</span>
            <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">original_block</span>
            <span class="k">return</span> <span class="kc">False</span>  <span class="c1"># equivalent to our test() routine returning False</span>

    <span class="k">class</span> <span class="nc">BoxExtension</span><span class="p">(</span><span class="n">Extension</span><span class="p">):</span>
        <span class="k">def</span> <span class="nf">extendMarkdown</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">md</span><span class="p">):</span>
            <span class="n">md</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">blockprocessors</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">BoxBlockProcessor</span><span class="p">(</span><span class="n">md</span><span class="o">.</span><span class="n">parser</span><span class="p">),</span> <span class="s1">'box'</span><span class="p">,</span> <span class="mi">175</span><span class="p">)</span>




这里的关键就是怎么匹配块的开头和结尾,利用的是两个正则,然后匹配之后就可以给这个块添加 html 标签和其他属性。

类似 vitepress 的规则 {#类似-vitepress-的规则}

看下我的代码:

class AlertBlockProcessor(BlockProcessor):
    RE_FENCE_START = r'^:{3}\s*primary\s*.*\n*|^:{3}\s*secondary\s*.*\n*|^:{3}\s*success\s*.*\n*|^:{3}\s*danger\s*.*\n*|^:{3}\s*warning\s*.*\n*|^:{3}\s*info\s*.*\n*'
    RE_FENCE_END = r'\n*:{3}$'

    <span class="n">icon_dict</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s1">'primary'</span><span class="p">:</span> <span class="s1">'info-circle'</span><span class="p">,</span>
        <span class="s1">'secondary'</span><span class="p">:</span> <span class="s1">'info-circle'</span><span class="p">,</span>
        <span class="s1">'success'</span><span class="p">:</span> <span class="s1">'info-circle'</span><span class="p">,</span>
        <span class="s1">'danger'</span><span class="p">:</span> <span class="s1">'warning'</span><span class="p">,</span>
        <span class="s1">'warning'</span><span class="p">:</span> <span class="s1">'warning'</span><span class="p">,</span>
        <span class="s1">'info'</span><span class="p">:</span> <span class="s1">'info-circle'</span>
    <span class="p">}</span>

    <span class="k">def</span> <span class="nf">test</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
        <span class="k">return</span> <span class="n">re</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_START</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">parent</span><span class="p">,</span> <span class="n">blocks</span><span class="p">):</span>
        <span class="c1"># print(blocks)</span>
        <span class="n">original_block</span> <span class="o">=</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
        <span class="n">first_blocks</span> <span class="o">=</span> <span class="n">original_block</span><span class="o">.</span><span class="n">split</span><span class="p">()</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">first_blocks</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
            <span class="n">title</span> <span class="o">=</span> <span class="n">first_blocks</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
        <span class="k">elif</span> <span class="nb">len</span><span class="p">(</span><span class="n">first_blocks</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
            <span class="n">title</span> <span class="o">=</span> <span class="s1">'Tip'</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">return</span> <span class="kc">False</span>
        <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_START</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
        <span class="c1"># print(blocks[0])</span>

        <span class="c1"># Find block with ending fence</span>
        <span class="k">for</span> <span class="n">block_num</span><span class="p">,</span> <span class="n">block</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">blocks</span><span class="p">):</span>
            <span class="c1"># print(re.search(self.RE_FENCE_END, block), block)</span>
            <span class="k">if</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_END</span><span class="p">,</span> <span class="n">block</span><span class="p">):</span>
                <span class="c1"># remove fence</span>
                <span class="n">blocks</span><span class="p">[</span><span class="n">block_num</span><span class="p">]</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">RE_FENCE_END</span><span class="p">,</span> <span class="s1">''</span><span class="p">,</span> <span class="n">block</span><span class="p">)</span>
                <span class="c1"># render fenced area inside a new div</span>
                <span class="n">e</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">SubElement</span><span class="p">(</span><span class="n">parent</span><span class="p">,</span> <span class="s1">'div'</span><span class="p">)</span>
                <span class="n">icon_elm</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s1">'i'</span><span class="p">)</span>
                <span class="n">strong_tag</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s1">'strong'</span><span class="p">)</span>
                <span class="n">title_elm</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s1">'p'</span><span class="p">)</span>
                <span class="n">class_value</span> <span class="o">=</span> <span class="s1">'alert alert-</span><span class="si">{}</span><span class="s1">'</span>
                <span class="n">flag</span> <span class="o">=</span> <span class="kc">False</span>
                <span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="p">[</span><span class="s1">'primary'</span><span class="p">,</span> <span class="s1">'secondary'</span><span class="p">,</span> <span class="s1">'success'</span><span class="p">,</span> <span class="s1">'danger'</span><span class="p">,</span> <span class="s1">'warning'</span><span class="p">,</span> <span class="s1">'info'</span><span class="p">]:</span>
                    <span class="k">if</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">original_block</span><span class="p">:</span>
                        <span class="n">e</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'class'</span><span class="p">,</span> <span class="n">class_value</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">key</span><span class="p">))</span>
                        <span class="n">e</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'role'</span><span class="p">,</span> <span class="s1">'alert'</span><span class="p">)</span>
                        <span class="n">icon_elm</span><span class="o">.</span><span class="n">set</span><span class="p">(</span><span class="s1">'class'</span><span class="p">,</span> <span class="s1">'fa fa-</span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">icon_dict</span><span class="p">[</span><span class="n">key</span><span class="p">]))</span>
                        <span class="n">strong_tag</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">icon_elm</span><span class="p">)</span>
                        <span class="n">span_elm</span> <span class="o">=</span> <span class="n">etree</span><span class="o">.</span><span class="n">Element</span><span class="p">(</span><span class="s1">'span'</span><span class="p">)</span>
                        <span class="n">span_elm</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">title</span>
                        <span class="n">strong_tag</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">span_elm</span><span class="p">)</span>
                        <span class="n">title_elm</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">strong_tag</span><span class="p">)</span>
                        <span class="n">e</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">title_elm</span><span class="p">)</span>
                        <span class="n">flag</span> <span class="o">=</span> <span class="kc">True</span>
                        <span class="k">break</span>
                <span class="k">if</span> <span class="ow">not</span> <span class="n">flag</span><span class="p">:</span>
                    <span class="k">return</span> <span class="kc">False</span>
                <span class="bp">self</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">parseBlocks</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">block_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">])</span>
                <span class="c1"># remove used blocks</span>
                <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">block_num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
                    <span class="n">blocks</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
                <span class="k">return</span> <span class="kc">True</span>  <span class="c1"># or could have had no return statement</span>
        <span class="c1"># No closing marker!  Restore and do nothing</span>
        <span class="n">blocks</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">original_block</span>
        <span class="k">return</span> <span class="kc">False</span>  <span class="c1"># equivalent to our test() routine returning False</span>




class AlertExtension(Extension):
    """
    生产Alert块
    """


    <span class="k">def</span> <span class="nf">extendMarkdown</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">md</span><span class="p">):</span>
        <span class="n">md</span><span class="o">.</span><span class="n">parser</span><span class="o">.</span><span class="n">blockprocessors</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">AlertBlockProcessor</span><span class="p">(</span><span class="n">md</span><span class="o">.</span><span class="n">parser</span><span class="p">),</span> <span class="s1">'alert'</span><span class="p">,</span> <span class="mi">178</span><span class="p">)</span>




这里是实现了一个跟 vitepress 的规则类似的效果。

我这里就是需要匹配 :::info 标题 这种格式,然后结尾必须是 :::,然后根据填写的级别去追加 class 属性和 role 属性。

测试和效果 {#测试和效果}

测试自定义规则 {#测试自定义规则}

我定义了三个规则,这里直接写一个用例同时验证三个规则,代码如下:

if __name__ == '__main__':
    import markdown

    <span class="n">m</span> <span class="o">=</span> <span class="n">markdown</span><span class="o">.</span><span class="n">Markdown</span><span class="p">(</span><span class="n">extensions</span><span class="o">=</span><span class="p">[</span>
        <span class="s1">'markdown.extensions.extra'</span><span class="p">,</span>
        <span class="n">DelExtension</span><span class="p">(),</span>
        <span class="n">IconExtension</span><span class="p">(),</span>
        <span class="n">AlertExtension</span><span class="p">()</span>
    <span class="p">])</span>

    <span class="n">t</span> <span class="o">=</span> <span class="s1">'''</span>




::: warning 提示


注意,这是一个演示效果,~~我是被删除的内容~~
:::
    '''


    <span class="n">html</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">convert</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
    <span class="nb">print</span><span class="p">(</span><span class="n">html</span><span class="p">)</span>




看一下输出的 html 效果:

<div class="alert alert-warning" role="alert">
<p><strong><i class="fa fa-warning"></i><span>提示</span></strong></p>
<p>注意,这是一个演示效果,<del>我是被删除的内容</del></p>
</div>

可以看到,代码块的规则符合预期,然后标签规则也符合预期,"删除文本"也是符合预期的。

演示效果 {#演示效果}

markdown 文本如下:

::: primary

这是一个没有标题的 alert
:::


::: primary


🎉 自定义标题


这是一个没有标题的 alert
:::


::: secondary


这是一个没有标题的 alert
:::


::: success 注意


这是一个演示效果,\~\~我是被删除的内容\~\~
:::


::: danger 注意


这是一个演示效果,\~\~我是被删除的内容\~\~
:::


::: warning 注意


这是一个演示效果,\~\~我是被删除的内容\~\~
:::


::: info 注意

`这是一个演示效果,~~我是被删除的内容~~
:::
`

渲染效果:
这是一个没有标题的 alert
🎉 自定义标题

这是一个没有标题的 alert
这是一个没有标题的 alert
注意

这是一个演示效果,~~我是被删除的内容~~
注意

这是一个演示效果,~~我是被删除的内容~~
注意

这是一个演示效果,~~我是被删除的内容~~
注意

这是一个演示效果,~~我是被删除的内容~~

代码组的效果 {#代码组的效果}

我还按照 vitepress 的渲染格式实现了代码组的效果,这个效果主要依靠 bootstrap 的效果,而且实现这个效果不仅仅是依靠 markdown 拓展,还需要写 js 代码来实现。

$ npx vitepress init
$ pnpm vitepress init
$ bunx vitepress init

官方文档 {#官方文档}

赞(0)
未经允许不得转载:工具盒子 » Python-Markdown 自定义拓展