You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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事件修改全局结构
    在这个事件中需要完成三件事:

    1. 更新全局文档列表:移除不需要保留的文档
    2. 更新每个toctree的条目:删除指向已移除文档/章节的链接
    3. 同步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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 12:00:02