5 Sphinx extension to generate automatic documentation of lexers,
6 formatters and filters.
8 :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
9 :license: BSD, see LICENSE for details.
14 from docutils
import nodes
15 from docutils
.statemachine
import ViewList
16 from docutils
.parsers
.rst
import Directive
17 from sphinx
.util
.nodes
import nested_parse_with_titles
58 class PygmentsDoc(Directive
):
60 A directive to collect all lexers/formatters/filters and generate
61 autoclass directives for them.
64 required_arguments
= 1
65 optional_arguments
= 0
66 final_argument_whitespace
= False
70 self
.filenames
= set()
71 if self
.arguments
[0] == 'lexers':
72 out
= self
.document_lexers()
73 elif self
.arguments
[0] == 'formatters':
74 out
= self
.document_formatters()
75 elif self
.arguments
[0] == 'filters':
76 out
= self
.document_filters()
77 elif self
.arguments
[0] == 'lexers_overview':
78 out
= self
.document_lexers_overview()
80 raise Exception('invalid argument for "pygmentsdoc" directive')
81 node
= nodes
.compound()
82 vl
= ViewList(out
.split('\n'), source
='')
83 nested_parse_with_titles(self
.state
, vl
, node
)
84 for fn
in self
.filenames
:
85 self
.state
.document
.settings
.record_dependencies
.add(fn
)
88 def document_lexers_overview(self
):
89 """Generate a tabular overview of all lexers.
91 The columns are the lexer name, the extensions handled by this lexer
92 (or "None"), the aliases and a link to the lexer class."""
93 from pip
._vendor
.pygments
.lexers
._mapping
import LEXERS
94 from pip
._vendor
.pygments
.lexers
import find_lexer_class
99 def format_link(name
, url
):
101 return f
'`{name} <{url}>`_'
104 for classname
, data
in sorted(LEXERS
.items(), key
=lambda x
: x
[1][1].lower()):
105 lexer_cls
= find_lexer_class(data
[1])
106 extensions
= lexer_cls
.filenames
+ lexer_cls
.alias_filenames
109 'name': format_link(data
[1], lexer_cls
.url
),
110 'extensions': ', '.join(extensions
).replace('*', '\\*').replace('_', '\\') or 'None',
111 'aliases': ', '.join(data
[2]),
112 'class': f
'{data[0]}.{classname}'
115 column_names
= ['name', 'extensions', 'aliases', 'class']
116 column_lengths
= [max([len(row
[column
]) for row
in table
if row
[column
]])
117 for column
in column_names
]
119 def write_row(*columns
):
120 """Format a table row"""
122 for l
, c
in zip(column_lengths
, columns
):
124 out
.append(c
.ljust(l
))
130 def write_seperator():
131 """Write a table separator row"""
132 sep
= ['='*c
for c
in column_lengths
]
133 return write_row(*sep
)
135 out
.append(write_seperator())
136 out
.append(write_row('Name', 'Extension(s)', 'Short name(s)', 'Lexer class'))
137 out
.append(write_seperator())
139 out
.append(write_row(
143 f
':class:`~{row["class"]}`'))
144 out
.append(write_seperator())
146 return '\n'.join(out
)
148 def document_lexers(self
):
149 from pip
._vendor
.pygments
.lexers
._mapping
import LEXERS
152 moduledocstrings
= {}
153 for classname
, data
in sorted(LEXERS
.items(), key
=lambda x
: x
[0]):
155 mod
= __import__(module
, None, None, [classname
])
156 self
.filenames
.add(mod
.__file
__)
157 cls
= getattr(mod
, classname
)
159 print("Warning: %s does not have a docstring." % classname
)
160 docstring
= cls
.__doc
__
161 if isinstance(docstring
, bytes):
162 docstring
= docstring
.decode('utf8')
163 modules
.setdefault(module
, []).append((
165 ', '.join(data
[2]) or 'None',
166 ', '.join(data
[3]).replace('*', '\\*').replace('_', '\\') or 'None',
167 ', '.join(data
[4]) or 'None',
169 if module
not in moduledocstrings
:
171 if isinstance(moddoc
, bytes):
172 moddoc
= moddoc
.decode('utf8')
173 moduledocstrings
[module
] = moddoc
175 for module
, lexers
in sorted(modules
.items(), key
=lambda x
: x
[0]):
176 if moduledocstrings
[module
] is None:
177 raise Exception("Missing docstring for %s" % (module
,))
178 heading
= moduledocstrings
[module
].splitlines()[4].strip().rstrip('.')
179 out
.append(MODULEDOC
% (module
, heading
, '-'*len(heading
)))
181 out
.append(LEXERDOC
% data
)
185 def document_formatters(self
):
186 from pip
._vendor
.pygments
.formatters
import FORMATTERS
189 for classname
, data
in sorted(FORMATTERS
.items(), key
=lambda x
: x
[0]):
191 mod
= __import__(module
, None, None, [classname
])
192 self
.filenames
.add(mod
.__file
__)
193 cls
= getattr(mod
, classname
)
194 docstring
= cls
.__doc
__
195 if isinstance(docstring
, bytes):
196 docstring
= docstring
.decode('utf8')
197 heading
= cls
.__name
__
198 out
.append(FMTERDOC
% (heading
, ', '.join(data
[2]) or 'None',
199 ', '.join(data
[3]).replace('*', '\\*') or 'None',
203 def document_filters(self
):
204 from pip
._vendor
.pygments
.filters
import FILTERS
207 for name
, cls
in FILTERS
.items():
208 self
.filenames
.add(sys
.modules
[cls
.__module
__].__file
__)
209 docstring
= cls
.__doc
__
210 if isinstance(docstring
, bytes):
211 docstring
= docstring
.decode('utf8')
212 out
.append(FILTERDOC
% (cls
.__name
__, name
, docstring
))
217 app
.add_directive('pygmentsdoc', PygmentsDoc
)