configext.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. from sphinx.domains import Domain, ObjType
  2. from sphinx.roles import XRefRole
  3. from sphinx.domains.std import GenericObject, StandardDomain
  4. from sphinx.directives import ObjectDescription
  5. from sphinx.util.nodes import clean_astext, make_refnode
  6. from sphinx.util import ws_re
  7. from sphinx import addnodes
  8. from sphinx.util.docfields import Field
  9. from docutils import nodes
  10. def get_id_from_cfg(text):
  11. '''
  12. Formats anchor ID from config option.
  13. '''
  14. if text[:6] == '$cfg[\'':
  15. text = text[6:]
  16. if text[-2:] == '\']':
  17. text = text[:-2]
  18. text = text.replace('[$i]', '')
  19. parts = text.split("']['")
  20. return parts
  21. class ConfigOption(ObjectDescription):
  22. indextemplate = 'configuration option; %s'
  23. parse_node = None
  24. has_arguments = True
  25. doc_field_types = [
  26. Field('default', label='Default value', has_arg=False,
  27. names=('default', )),
  28. Field('type', label='Type', has_arg=False,
  29. names=('type',)),
  30. ]
  31. def handle_signature(self, sig, signode):
  32. signode.clear()
  33. signode += addnodes.desc_name(sig, sig)
  34. # normalize whitespace like XRefRole does
  35. name = ws_re.sub('', sig)
  36. return name
  37. def add_target_and_index(self, name, sig, signode):
  38. targetparts = get_id_from_cfg(name)
  39. targetname = 'cfg_%s' % '_'.join(targetparts)
  40. signode['ids'].append(targetname)
  41. self.state.document.note_explicit_target(signode)
  42. indextype = 'single'
  43. # Generic index entries
  44. indexentry = self.indextemplate % (name,)
  45. self.indexnode['entries'].append((indextype, indexentry,
  46. targetname, targetname))
  47. self.indexnode['entries'].append((indextype, name,
  48. targetname, targetname))
  49. # Server section
  50. if targetparts[0] == 'Servers' and len(targetparts) > 1:
  51. indexname = ', '.join(targetparts[1:])
  52. self.indexnode['entries'].append((indextype, 'server configuration; %s' % indexname,
  53. targetname, targetname))
  54. self.indexnode['entries'].append((indextype, indexname,
  55. targetname, targetname))
  56. else:
  57. indexname = ', '.join(targetparts)
  58. self.indexnode['entries'].append((indextype, indexname,
  59. targetname, targetname))
  60. self.env.domaindata['config']['objects'][self.objtype, name] = \
  61. self.env.docname, targetname
  62. class ConfigSectionXRefRole(XRefRole):
  63. """
  64. Cross-referencing role for configuration sections (adds an index entry).
  65. """
  66. def result_nodes(self, document, env, node, is_ref):
  67. if not is_ref:
  68. return [node], []
  69. varname = node['reftarget']
  70. tgtid = 'index-%s' % env.new_serialno('index')
  71. indexnode = addnodes.index()
  72. indexnode['entries'] = [
  73. ('single', varname, tgtid, varname),
  74. ('single', 'configuration section; %s' % varname, tgtid, varname)
  75. ]
  76. targetnode = nodes.target('', '', ids=[tgtid])
  77. document.note_explicit_target(targetnode)
  78. return [indexnode, targetnode, node], []
  79. class ConfigSection(ObjectDescription):
  80. indextemplate = 'configuration section; %s'
  81. parse_node = None
  82. def handle_signature(self, sig, signode):
  83. if self.parse_node:
  84. name = self.parse_node(self.env, sig, signode)
  85. else:
  86. signode.clear()
  87. signode += addnodes.desc_name(sig, sig)
  88. # normalize whitespace like XRefRole does
  89. name = ws_re.sub('', sig)
  90. return name
  91. def add_target_and_index(self, name, sig, signode):
  92. targetname = '%s-%s' % (self.objtype, name)
  93. signode['ids'].append(targetname)
  94. self.state.document.note_explicit_target(signode)
  95. if self.indextemplate:
  96. colon = self.indextemplate.find(':')
  97. if colon != -1:
  98. indextype = self.indextemplate[:colon].strip()
  99. indexentry = self.indextemplate[colon+1:].strip() % (name,)
  100. else:
  101. indextype = 'single'
  102. indexentry = self.indextemplate % (name,)
  103. self.indexnode['entries'].append((indextype, indexentry,
  104. targetname, targetname))
  105. self.env.domaindata['config']['objects'][self.objtype, name] = \
  106. self.env.docname, targetname
  107. class ConfigOptionXRefRole(XRefRole):
  108. """
  109. Cross-referencing role for configuration options (adds an index entry).
  110. """
  111. def result_nodes(self, document, env, node, is_ref):
  112. if not is_ref:
  113. return [node], []
  114. varname = node['reftarget']
  115. tgtid = 'index-%s' % env.new_serialno('index')
  116. indexnode = addnodes.index()
  117. indexnode['entries'] = [
  118. ('single', varname, tgtid, varname),
  119. ('single', 'configuration option; %s' % varname, tgtid, varname)
  120. ]
  121. targetnode = nodes.target('', '', ids=[tgtid])
  122. document.note_explicit_target(targetnode)
  123. return [indexnode, targetnode, node], []
  124. class ConfigFileDomain(Domain):
  125. name = 'config'
  126. label = 'Config'
  127. object_types = {
  128. 'option': ObjType('config option', 'option'),
  129. 'section': ObjType('config section', 'section'),
  130. }
  131. directives = {
  132. 'option': ConfigOption,
  133. 'section': ConfigSection,
  134. }
  135. roles = {
  136. 'option': ConfigOptionXRefRole(),
  137. 'section': ConfigSectionXRefRole(),
  138. }
  139. initial_data = {
  140. 'objects': {}, # (type, name) -> docname, labelid
  141. }
  142. def clear_doc(self, docname):
  143. for key, (fn, _) in self.data['objects'].items():
  144. if fn == docname:
  145. del self.data['objects'][key]
  146. def resolve_xref(self, env, fromdocname, builder,
  147. typ, target, node, contnode):
  148. docname, labelid = self.data['objects'].get((typ, target), ('', ''))
  149. if not docname:
  150. return None
  151. else:
  152. return make_refnode(builder, fromdocname, docname,
  153. labelid, contnode)
  154. def get_objects(self):
  155. for (type, name), info in self.data['objects'].iteritems():
  156. yield (name, name, type, info[0], info[1],
  157. self.object_types[type].attrs['searchprio'])
  158. def setup(app):
  159. app.add_domain(ConfigFileDomain)