You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
4.5 KiB

4 years ago
  1. # -*- coding: utf-8 -*-
  2. """
  3. pygments.sphinxext
  4. ~~~~~~~~~~~~~~~~~~
  5. Sphinx extension to generate automatic documentation of lexers,
  6. formatters and filters.
  7. :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
  8. :license: BSD, see LICENSE for details.
  9. """
  10. import sys
  11. from docutils import nodes
  12. from docutils.statemachine import ViewList
  13. from docutils.parsers.rst import Directive
  14. from sphinx.util.nodes import nested_parse_with_titles
  15. MODULEDOC = '''
  16. .. module:: %s
  17. %s
  18. %s
  19. '''
  20. LEXERDOC = '''
  21. .. class:: %s
  22. :Short names: %s
  23. :Filenames: %s
  24. :MIME types: %s
  25. %s
  26. '''
  27. FMTERDOC = '''
  28. .. class:: %s
  29. :Short names: %s
  30. :Filenames: %s
  31. %s
  32. '''
  33. FILTERDOC = '''
  34. .. class:: %s
  35. :Name: %s
  36. %s
  37. '''
  38. class PygmentsDoc(Directive):
  39. """
  40. A directive to collect all lexers/formatters/filters and generate
  41. autoclass directives for them.
  42. """
  43. has_content = False
  44. required_arguments = 1
  45. optional_arguments = 0
  46. final_argument_whitespace = False
  47. option_spec = {}
  48. def run(self):
  49. self.filenames = set()
  50. if self.arguments[0] == 'lexers':
  51. out = self.document_lexers()
  52. elif self.arguments[0] == 'formatters':
  53. out = self.document_formatters()
  54. elif self.arguments[0] == 'filters':
  55. out = self.document_filters()
  56. else:
  57. raise Exception('invalid argument for "pygmentsdoc" directive')
  58. node = nodes.compound()
  59. vl = ViewList(out.split('\n'), source='')
  60. nested_parse_with_titles(self.state, vl, node)
  61. for fn in self.filenames:
  62. self.state.document.settings.record_dependencies.add(fn)
  63. return node.children
  64. def document_lexers(self):
  65. from pygments.lexers._mapping import LEXERS
  66. out = []
  67. modules = {}
  68. moduledocstrings = {}
  69. for classname, data in sorted(LEXERS.items(), key=lambda x: x[0]):
  70. module = data[0]
  71. mod = __import__(module, None, None, [classname])
  72. self.filenames.add(mod.__file__)
  73. cls = getattr(mod, classname)
  74. if not cls.__doc__:
  75. print("Warning: %s does not have a docstring." % classname)
  76. docstring = cls.__doc__
  77. if isinstance(docstring, bytes):
  78. docstring = docstring.decode('utf8')
  79. modules.setdefault(module, []).append((
  80. classname,
  81. ', '.join(data[2]) or 'None',
  82. ', '.join(data[3]).replace('*', '\\*').replace('_', '\\') or 'None',
  83. ', '.join(data[4]) or 'None',
  84. docstring))
  85. if module not in moduledocstrings:
  86. moddoc = mod.__doc__
  87. if isinstance(moddoc, bytes):
  88. moddoc = moddoc.decode('utf8')
  89. moduledocstrings[module] = moddoc
  90. for module, lexers in sorted(modules.items(), key=lambda x: x[0]):
  91. if moduledocstrings[module] is None:
  92. raise Exception("Missing docstring for %s" % (module,))
  93. heading = moduledocstrings[module].splitlines()[4].strip().rstrip('.')
  94. out.append(MODULEDOC % (module, heading, '-'*len(heading)))
  95. for data in lexers:
  96. out.append(LEXERDOC % data)
  97. return ''.join(out)
  98. def document_formatters(self):
  99. from pygments.formatters import FORMATTERS
  100. out = []
  101. for classname, data in sorted(FORMATTERS.items(), key=lambda x: x[0]):
  102. module = data[0]
  103. mod = __import__(module, None, None, [classname])
  104. self.filenames.add(mod.__file__)
  105. cls = getattr(mod, classname)
  106. docstring = cls.__doc__
  107. if isinstance(docstring, bytes):
  108. docstring = docstring.decode('utf8')
  109. heading = cls.__name__
  110. out.append(FMTERDOC % (heading, ', '.join(data[2]) or 'None',
  111. ', '.join(data[3]).replace('*', '\\*') or 'None',
  112. docstring))
  113. return ''.join(out)
  114. def document_filters(self):
  115. from pygments.filters import FILTERS
  116. out = []
  117. for name, cls in FILTERS.items():
  118. self.filenames.add(sys.modules[cls.__module__].__file__)
  119. docstring = cls.__doc__
  120. if isinstance(docstring, bytes):
  121. docstring = docstring.decode('utf8')
  122. out.append(FILTERDOC % (cls.__name__, name, docstring))
  123. return ''.join(out)
  124. def setup(app):
  125. app.add_directive('pygmentsdoc', PygmentsDoc)