Last active
October 20, 2015 15:07
-
-
Save tk0miya/e36dfcff629cd34a4f58 to your computer and use it in GitHub Desktop.
new_numfig.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
new_numfig.py: yet another numfig.py | |
------------------------------------- | |
This is Sphinx extension for numbering figures and tables automatically. | |
what's difference? | |
------------------- | |
- Support numbering tables | |
- Rename :num: role to :ref:num: and support HTML format | |
- Numbering over multiple .rst files (in HTML) | |
- Support branch numbering (cf. 1-1, 1-2, 2-1) with numbered toctree (in HTML) | |
- Add extensivility for third party extensions | |
Note | |
----- | |
this is implementation for experimentation (I'll send feature request to sphinx itself). | |
it does not handle any errors. | |
it's absolutely no warranty | |
LICENSE | |
-------- | |
Apache 2.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from docutils import nodes | |
from collections import defaultdict | |
from sphinx.roles import XRefRole | |
from sphinx.ext.graphviz import graphviz | |
counter = defaultdict(int) | |
class num_ref(nodes.reference): | |
pass | |
def latex_visit_num_ref(self, node): | |
domain = self.builder.env.domains['std'] | |
target_docname, labelid = domain.data['anonlabels'].get(node['reftarget']) | |
self.body.append('\\ref{%s:%s}' % (target_docname, labelid)) | |
raise nodes.SkipNode | |
def default_caption_updater(node, prefix, secnum, fignum): | |
for caption in node.traverse(nodes.caption): | |
if secnum: | |
caption[0] = nodes.Text("%s%s-%s: %s" % (prefix, secnum, fignum, caption[0])) | |
else: | |
caption[0] = nodes.Text("%s%s: %s" % (prefix, fignum, caption[0])) | |
return True | |
def graphviz_caption_updater(node, prefix, secnum, fignum): | |
if secnum: | |
node['caption'] = "%s%s-%d: %s" % (prefix, secnum, fignum, node['caption']) | |
else: | |
node['caption'] = "%s%d: %s" % (prefix, fignum, node['caption']) | |
return True | |
def table_title_updater(node, prefix, secnum, fignum): | |
if isinstance(node[0], nodes.title): | |
if secnum: | |
node[0][0] = nodes.Text("%s%s-%d: %s" % (prefix, secnum, fignum, node[0][0])) | |
else: | |
node[0][0] = nodes.Text("%s%d: %s" % (prefix, fignum, node[0][0])) | |
return True | |
else: | |
return False | |
def html_visit_num_ref(self, node): | |
builder = self.builder | |
domain = builder.env.domains['std'] | |
node['refexplicit'] = True | |
xref = domain.resolve_xref(builder.env, builder.current_docname, builder, | |
'ref', node['reftarget'], node, node) | |
if xref is None: | |
builder.warn('Unknown label: %s' % node['reftarget']) | |
node.parent.remove(node) | |
else: | |
target_docname, labelid = domain.data['anonlabels'].get(node['reftarget']) | |
fignum = builder.env.doc_fignum[target_docname] | |
secnumbers = builder.env.toc_secnumbers.get(target_docname) | |
if secnumbers: | |
secnum = secnumbers[''][0] | |
else: | |
secnum = '' | |
target_doctree = builder.env.get_doctree(target_docname) | |
assign_fignumbers(builder, target_doctree, builder.current_docname, secnum, fignum) | |
for target in target_doctree.traverse(nodes.Element): | |
if node['reftarget'] in target['names']: | |
while 'fignum' not in target: | |
target = target.parent | |
xref[0] = nodes.emphasis(text=target['fignum']) | |
break | |
else: | |
builder.warn('Could not resolve fignumber: %s' % node['reftarget']) | |
xref[0] = nodes.emphasis(text='??') | |
node.replace_self(xref) | |
xref.walkabout(self) | |
raise nodes.SkipNode | |
def assign_fignumbers(app, doctree, docname, secnum, fignum): | |
config = app.env.config | |
for node in doctree.traverse(nodes.Node): | |
if node.__class__ in config.number_caption_rules: | |
type, caption_updater = config.number_caption_rules[node.__class__] | |
prefix = config.numbered_caption_prefixes[type] | |
if caption_updater is None: | |
caption_updater = default_caption_updater | |
if caption_updater(node, prefix, secnum, fignum): | |
if secnum: | |
node['fignum'] = "%s-%d" % (secnum, fignum) | |
else: | |
node['fignum'] = fignum | |
fignum += 1 | |
return fignum | |
def on_doctree_resolved(app, doctree, docname): | |
if app.builder.name == 'latex': | |
return | |
secnumbers = app.env.toc_secnumbers.get(docname) | |
if secnumbers: | |
secnum = secnumbers[''][0] | |
else: | |
secnum = '' | |
if secnum in app.env.fignum: | |
fignum = app.env.fignum[secnum] | |
else: | |
fignum = app.env.fignum[secnum] = 1 | |
app.env.doc_fignum[docname] = fignum | |
next_fignum = assign_fignumbers(app, doctree, docname, secnum, fignum) | |
app.env.fignum[fignum] = next_fignum | |
def on_builder_inited(app): | |
app.builder.env.fignum = {} | |
app.builder.env.doc_fignum = {} | |
def setup(app): | |
app.add_node(num_ref, | |
html=(html_visit_num_ref, None), | |
latex=(latex_visit_num_ref, None)) | |
app.add_role('ref:num', XRefRole(nodeclass=num_ref)) | |
app.connect("builder-inited", on_builder_inited) | |
app.connect('doctree-resolved', on_doctree_resolved) | |
# config values | |
number_caption_rules = {nodes.figure: ('image', None), | |
graphviz: ('image', graphviz_caption_updater), | |
nodes.table: ('table', table_title_updater)} | |
numbered_caption_prefixes = {'image': 'Fig.', | |
'table': 'Table '} | |
app.add_config_value('number_caption_rules', number_caption_rules, 'env') | |
app.add_config_value('numbered_caption_prefixes', numbered_caption_prefixes, 'env') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks, it works great!
One thing that you should explain is that the reference no longer takes the
#
before the label:like this
:ref:num:
my-fig-ref``