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