如何实现可嵌套service_card的Sphinx service_card_wrapper指令?
问题:实现可嵌套service_card的Sphinx指令service_card_wrapper
我们需要创建一个Sphinx指令service_card_wrapper,支持嵌套已实现的service_card指令,示例用法如下:
Directives ========== .. service_card_wrapper:: .. service_card:: .. service_card::
目前service_card指令已实现,但无法将其内容渲染到service_card_wrapper中,期望生成的HTML结构为:
<div class="service-card-wrapper"> <div class="service-card"> Content 1 </div> <div class="service-card"> Content 2 </div> </div>
已实现的service_card指令代码如下:
from docutils import nodes from docutils.parsers.rst import Directive from docutils.parsers.rst import directives from sphinx.util import logging LOG = logging.getLogger(__name__) class service_card(nodes.General, nodes.Element): pass class ServiceCard(Directive): node_class = service_card option_spec = { # 'service_type': directives.unchanged_required, } has_content = False def run(self): node = self.node_class() # node['service_type'] = self.options.get('service_type') return [node] def service_card_html(self, node): # This method renders containers per each service of the category with all # links as individual list items data = ''' <div class="card"> <h5 class="card-header">Featured</h5> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div>''' self.body.append(data) raise nodes.SkipNode def setup(app): app.add_node(service_card, html=(service_card_html, None)) app.add_directive("service_card", ServiceCard) return { 'version': '0.1', 'parallel_read_safe': True, 'parallel_write_safe': True, }
解决方案
以下是完整的实现代码,包含service_card_wrapper的定义、渲染逻辑,同时调整了service_card的HTML类名以匹配期望结构:
from docutils import nodes from docutils.parsers.rst import Directive from docutils.parsers.rst import directives from sphinx.util import logging LOG = logging.getLogger(__name__) class service_card(nodes.General, nodes.Element): pass class service_card_wrapper(nodes.General, nodes.Element): pass class ServiceCard(Directive): node_class = service_card option_spec = { # 'service_type': directives.unchanged_required, } has_content = False def run(self): node = self.node_class() # node['service_type'] = self.options.get('service_type') return [node] class ServiceCardWrapper(Directive): node_class = service_card_wrapper has_content = True # 允许指令包含嵌套内容 option_spec = {} def run(self): node = self.node_class() # 解析嵌套的内容,生成子节点并添加到wrapper节点中 self.state.nested_parse(self.content, self.content_offset, node) return [node] def service_card_html(self, node): # 修改类名为service-card,匹配期望的HTML结构 data = ''' <div class="service-card"> <h5 class="card-header">Featured</h5> <div class="card-body"> <h5 class="card-title">Special title treatment</h5> <p class="card-text">With supporting text below as a natural lead-in to additional content.</p> <a href="#" class="btn btn-primary">Go somewhere</a> </div> </div>''' self.body.append(data) raise nodes.SkipNode def service_card_wrapper_html(self, node): # 添加wrapper起始标签 self.body.append('<div class="service-card-wrapper">') # 渲染所有子节点(即嵌套的service_card) self.visit_children(node) # 添加wrapper结束标签 self.body.append('</div>') raise nodes.SkipNode def setup(app): app.add_node(service_card, html=(service_card_html, None)) app.add_node(service_card_wrapper, html=(service_card_wrapper_html, None)) app.add_directive("service_card", ServiceCard) app.add_directive("service_card_wrapper", ServiceCardWrapper) return { 'version': '0.1', 'parallel_read_safe': True, 'parallel_write_safe': True, }
关键说明
ServiceCardWrapper指令核心:设置has_content=True,并通过self.state.nested_parse解析指令内的嵌套内容,将生成的子节点挂载到service_card_wrapper节点上- wrapper渲染逻辑:
service_card_wrapper_html函数先输出容器起始标签,再调用self.visit_children(node)触发所有子节点的渲染,最后输出结束标签,确保子节点的HTML被正确包裹 - 类名调整:将
service_card渲染的HTML类从card改为service-card,完全匹配期望的结构
内容的提问来源于stack exchange,提问作者allegoric
相关产品推荐
相关产品推荐

