如何在Sphinx扩展中更新目录(toctree)?解决章节过滤后toc未同步问题
解决Sphinx扩展中toctree与文档内容不同步的问题
核心原因
doctree-read事件仅处理单个文档的语法树,而Sphinx的全局toctree结构是在env-updated事件前就已构建完成的,所以修改单个doctree不会自动同步更新侧边栏的toctree。
解决方案
1. 选择正确的事件:env-updated
这个事件的触发时机是:所有文档解析完成、项目环境更新完毕,但还未开始生成HTML输出。此时修改全局的文档列表和toctree结构,能确保最终的侧边栏目录与过滤后的文档内容完全一致。
2. 具体实现步骤
第一步:收集需保留的文档/章节信息
在文档解析阶段(比如doctree-read或document-read),根据你定义的标记(比如自定义指令、元数据)和sphinx-build传入的参数,把需要保留的文档和章节信息存储到Sphinx环境(env)中,方便后续过滤使用。第二步:监听
env-updated事件修改全局结构
在这个事件中需要完成三件事:- 更新全局文档列表:移除不需要保留的文档
- 更新每个toctree的条目:删除指向已移除文档/章节的链接
- 同步
toctree_includes映射:确保父子文档的包含关系正确
3. 代码示例
def collect_keep_info(app, doctree, docname): # 示例:从文档元数据中读取标记,判断是否保留该文档 # 假设用 :meta: keep="true" 标记需保留的内容 keep = False for node in doctree.traverse(): if hasattr(node, 'get') and node.get('meta'): meta = node.get('meta') if meta.get('keep') == 'true': keep = True break # 将结果存储到环境变量中 if not hasattr(app.env, 'keep_docs'): app.env.keep_docs = set() if keep: app.env.keep_docs.add(docname) def filter_tocs_and_docs(app, env): # 从配置中获取传入的过滤参数 filter_param = app.config.my_filter_param # 结合参数和收集的信息确定需保留的文档 keep_docs = env.keep_docs if hasattr(env, 'keep_docs') else set(env.found_docs) # 更新全局文档列表 env.found_docs = keep_docs # 更新每个toctree的条目 for docname, toctree in list(env.tocs.items()): if docname not in keep_docs: del env.tocs[docname] continue # 过滤toctree中指向已移除文档的条目 filtered_entries = [] for entry in toctree: # entry格式通常为(文档名, 标题, 层级, 是否隐藏, ...) if entry[0] in keep_docs: filtered_entries.append(entry) env.tocs[docname] = filtered_entries # 更新toctree_includes映射 for parent in list(env.toctree_includes.keys()): if parent not in keep_docs: del env.toctree_includes[parent] continue filtered_children = [child for child in env.toctree_includes[parent] if child in keep_docs] env.toctree_includes[parent] = filtered_children def setup(app): # 添加自定义配置参数,接收sphinx-build传入的参数(如通过-D my_filter_param=xxx) app.add_config_value('my_filter_param', '', 'env') # 收集需保留的文档信息 app.connect('doctree-read', collect_keep_info) # 过滤文档和toctree app.connect('env-updated', filter_tocs_and_docs)
4. 单文档内章节过滤的补充
如果是要过滤单个文档内的子章节(而非整个文档),除了在doctree-read中移除对应节点,还需要在env-updated中找到该文档对应的toctree条目,修改其标题和链接(如果子章节是toctree的直接条目),确保toctree生成时能识别到文档内的章节变化。
内容的提问来源于stack exchange,提问作者fws
相关产品推荐
相关产品推荐

