1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-22 13:40:44 -08:00
emacs/mps/manual/source/extensions/mps.py
Gareth Rees e2b456f235 Replace "deprecated since version 1.111" by "deprecated starting with version 1.111" when appropriate.
Copied from Perforce
 Change: 179986
 ServerID: perforce.ravenbrook.com
2012-10-20 21:18:40 +01:00

261 lines
7.6 KiB
Python

'''
Sphinx extensions for the MPS documentation.
See <http://sphinx.pocoo.org/extensions.html>
'''
from collections import defaultdict
import re
from docutils import nodes, transforms
from sphinx import addnodes
from sphinx.directives.other import VersionChange
from sphinx.domains import Domain
from sphinx.roles import XRefRole
from sphinx.util.compat import Directive, make_admonition
from sphinx.util.nodes import set_source_info, process_index_entry
from sphinx.locale import versionlabels
versionlabels['deprecatedstarting'] = 'Deprecated starting with version %s'
class MpsDomain(Domain):
label = 'MPS'
name = 'mps'
class deprecated(addnodes.versionmodified):
pass
class DeprecatedDirective(VersionChange):
# Copied from VersionChange: can't be subclassed because of the
# need to make a deprecated node rather than a versionmodified
# node.
def run(self):
node = deprecated()
node.document = self.state.document
set_source_info(self, node)
node['type'] = self.name
node['version'] = self.arguments[0]
if len(self.arguments) == 2:
inodes, messages = self.state.inline_text(self.arguments[1],
self.lineno+1)
node.extend(inodes)
if self.content:
self.state.nested_parse(self.content, self.content_offset, node)
ret = [node] + messages
else:
ret = [node]
env = self.state.document.settings.env
# XXX should record node.source as well
env.note_versionchange(node['type'], node['version'], node, node.line)
return ret
def version_compare(*args):
return cmp(*[map(int, v.split('.')) for v in args])
def visit_deprecated_node(self, node):
if (node['type'] == 'deprecated'
and version_compare(node['version'], self.builder.config.version) >= 0):
node['type'] = 'deprecatedstarting'
self.visit_versionmodified(node)
def depart_deprecated_node(self, node):
self.depart_versionmodified(node)
class Admonition(nodes.Admonition, nodes.Element):
pass
class AdmonitionDirective(Directive):
cls = nodes.note
label = 'Admonition'
has_content = True
def run(self):
ad = make_admonition(self.cls, self.name, [self.label], self.options,
self.content, self.lineno, self.content_offset,
self.block_text, self.state, self.state_machine)
return ad
class PluralDirective(AdmonitionDirective):
def run(self):
ad = super(PluralDirective, self).run()
refs = sum(1 for node in ad[0].children[1].children
if isinstance(node, addnodes.pending_xref)
or isinstance(node, nodes.Referential))
if refs > 1:
assert(isinstance(ad[0].children[0], nodes.title))
ad[0].children[0].children[0] = nodes.Text(self.plural)
return ad
def visit_admonition_node(self, node):
self.visit_admonition(node)
def depart_admonition_node(self, node):
self.depart_admonition(node)
class aka(Admonition):
pass
class AkaDirective(AdmonitionDirective):
cls = aka
label = 'Also known as'
class bibref(Admonition):
pass
class BibrefDirective(PluralDirective):
cls = bibref
label = 'Related publication'
plural = 'Related publications'
class historical(Admonition):
pass
class HistoricalDirective(AdmonitionDirective):
cls = historical
label = 'Historical note'
class link(Admonition):
pass
class LinkDirective(PluralDirective):
cls = link
label = 'Related link'
plural = 'Related links'
class note(Admonition):
pass
class NoteDirective(AdmonitionDirective):
cls = note
label = 'Note'
plural = 'Notes'
def run(self):
ad = super(NoteDirective, self).run()
c = ad[0].children
assert(isinstance(c[0], nodes.title))
if len(c) == 1: return ad
if (isinstance(c[1], nodes.enumerated_list)
and sum(1 for _ in c[1].traverse(nodes.list_item)) > 1
or isinstance(c[1], nodes.footnote)
and sum(1 for _ in ad[0].traverse(nodes.footnote)) > 1):
c[0].children[0] = nodes.Text(self.plural)
return ad
class opposite(Admonition):
pass
class OppositeDirective(PluralDirective):
cls = opposite
label = 'Opposite term'
plural = 'Opposite terms'
class relevance(Admonition):
pass
class RelevanceDirective(AdmonitionDirective):
cls = relevance
label = 'Relevance to memory management'
class see(Admonition):
pass
class SeeDirective(AdmonitionDirective):
cls = see
label = 'See'
class similar(Admonition):
pass
class SimilarDirective(PluralDirective):
cls = similar
label = 'Similar term'
plural = 'Similar terms'
class specific(Admonition):
pass
class SpecificDirective(AdmonitionDirective):
domain = 'mps'
cls = specific
label = 'In the MPS'
class topics(Admonition):
pass
class TopicsDirective(PluralDirective):
cls = topics
label = 'Topic'
plural = 'Topics'
all_admonitions = [
AkaDirective,
BibrefDirective,
HistoricalDirective,
LinkDirective,
NoteDirective,
OppositeDirective,
RelevanceDirective,
SeeDirective,
SimilarDirective,
SpecificDirective,
TopicsDirective]
see_only_ids = set()
xref_ids = defaultdict(list)
class GlossaryTransform(transforms.Transform):
default_priority = 999
sense_re = re.compile(r'(.*)\s+(\([0-9]+\))$')
def apply(self):
global see_only_ids, xref_ids
for target in self.document.traverse(nodes.term):
target.children = list(self.edit_children(target))
for target in self.document.traverse(addnodes.pending_xref):
if target['reftype'] == 'term':
xref_ids['term-{}'.format(target['reftarget'])].append((target.source, target.line))
c = target.children
if len(c) == 1 and isinstance(c[0], nodes.emphasis):
c[0].children = list(self.edit_children(c[0]))
for target in self.document.traverse(nodes.definition_list_item):
ids = set()
for c in target.children:
if isinstance(c, nodes.term):
ids = set(c['ids'])
if (isinstance(c, nodes.definition)
and len(c.children) == 1
and isinstance(c.children[0], see)):
see_only_ids |= ids
def edit_children(self, target):
for e in target.children:
if not isinstance(e, nodes.Text):
yield e
continue
m = self.sense_re.match(e)
if not m:
yield e
continue
yield nodes.Text(m.group(1))
yield nodes.superscript(text = m.group(2))
def warn_indirect_terms(app, exception):
if not exception:
for i in see_only_ids:
for doc, line in xref_ids[i]:
print('Cross-reference to {} at {} line {}.'.format(i, doc, line))
def setup(app):
app.add_domain(MpsDomain)
app.add_transform(GlossaryTransform)
app.connect('build-finished', warn_indirect_terms)
visit = (visit_admonition_node, depart_admonition_node)
for d in all_admonitions:
app.add_node(d.cls, html = visit, latex = visit, text = visit)
try:
app.add_directive_to_domain(d.domain, d.cls.__name__, d)
except AttributeError:
app.add_directive(d.cls.__name__, d)
visit = (visit_deprecated_node, depart_deprecated_node)
app.add_node(deprecated, html = visit, latex = visit, text = visit)
app.add_directive('deprecated', DeprecatedDirective)