如何制作也是外部 link 的自定义 sphinx 角色?

how to make a custom sphinx role that is also an external link?

我正在尝试扩展 Sphinx 以拥有一个名为 newcontrib 的新内联角色。它应该做的是获取其内容并 (1) 添加一些额外的文本,(2) 在呈现的输出中将文本设为粗体,以及 (3) 将文本设为 link,其目标是原始文本角色的内容。示例:

:newcontrib:`Jane Doe`

最终应该像

一样被解析
`**new contributor Jane Doe** <Jane Doe_>`_

目标在另一个文件 (names.inc) 中定义,看起来像:

.. _Jane Doe: https://jane-does-website.com

我目前的尝试(大致基于 this example)是这样的:

from docutils.nodes import reference, strong, target
from sphinx.addnodes import pending_xref


def newcontrib_role(name, rawtext, text, lineno, inliner, options={},
                    content=[]):
    """Create a role to highlight new contributors in changelog entries."""
    newcontrib = f'new contributor {text}'
    targ = f' <{text}_>'
    rawtext = f'`{newcontrib}{targ}`_'
    options.update(name=newcontrib, refname=text.lower())
    strong_node = strong(rawtext, newcontrib)
    node = reference('', '', strong_node, **options)
    return [node], []


def setup(app):
    app.add_role('newcontrib', newcontrib_role)
    return

我也尝试过使用 pending_xref() 而不是 reference(),并尝试明确创建一个 target 节点并将其添加为兄弟节点。结果因我的尝试而异;有时我在构建过程中遇到错误(缺少属性 refname、refdomain、reftype...),其他时候我构建成功但 link 变为 url-of-current-page#jane-doe。我还简要地尝试了子类化 XRefRole 而不是定义角色函数...对任何方法都开放。

最终起作用的是:

def newcontrib_role(name, rawtext, text, lineno, inliner, options={},
                    content=[]):
    """Create a role to highlight new contributors in changelog entries."""
    newcontrib = f'new contributor {text}'
    alias_text = f' <{text}_>'
    rawtext = f'`{newcontrib}{alias_text}`_'
    refname = text.lower()
    strong_node = strong(rawtext, newcontrib)
    target_node = target(alias_text, refname=refname, names=[newcontrib])
    target_node.indirect_reference_name = text
    options.update(name=newcontrib, refname=refname)
    ref_node = reference('', '', strong_node, **options)
    ref_node[0].rawsource = rawtext
    inliner.document.note_indirect_target(target_node)
    inliner.document.note_refname(ref_node)
    return [ref_node, target_node], []

(加上问题中的setup(app)函数)

特别是,我以前没有尝试过的关键步骤是 inliner.document.note_indirect_targetinliner.document.note_refname。我从 how docutils parses links.

得到了灵感