| Viewing file:  sphinxext.py (7.79 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
"""pygments.sphinxext
 ~~~~~~~~~~~~~~~~~~
 
 Sphinx extension to generate automatic documentation of lexers,
 formatters and filters.
 
 :copyright: Copyright 2006-2024 by the Pygments team, see AUTHORS.
 :license: BSD, see LICENSE for details.
 """
 
 import sys
 
 from docutils import nodes
 from docutils.statemachine import ViewList
 from docutils.parsers.rst import Directive
 from sphinx.util.nodes import nested_parse_with_titles
 
 
 MODULEDOC = '''
 .. module:: %s
 
 %s
 %s
 '''
 
 LEXERDOC = '''
 .. class:: %s
 
 :Short names: %s
 :Filenames:   %s
 :MIME types:  %s
 
 %s
 
 %s
 
 '''
 
 FMTERDOC = '''
 .. class:: %s
 
 :Short names: %s
 :Filenames: %s
 
 %s
 
 '''
 
 FILTERDOC = '''
 .. class:: %s
 
 :Name: %s
 
 %s
 
 '''
 
 
 class PygmentsDoc(Directive):
 """
 A directive to collect all lexers/formatters/filters and generate
 autoclass directives for them.
 """
 has_content = False
 required_arguments = 1
 optional_arguments = 0
 final_argument_whitespace = False
 option_spec = {}
 
 def run(self):
 self.filenames = set()
 if self.arguments[0] == 'lexers':
 out = self.document_lexers()
 elif self.arguments[0] == 'formatters':
 out = self.document_formatters()
 elif self.arguments[0] == 'filters':
 out = self.document_filters()
 elif self.arguments[0] == 'lexers_overview':
 out = self.document_lexers_overview()
 else:
 raise Exception('invalid argument for "pygmentsdoc" directive')
 node = nodes.compound()
 vl = ViewList(out.split('\n'), source='')
 nested_parse_with_titles(self.state, vl, node)
 for fn in self.filenames:
 self.state.document.settings.record_dependencies.add(fn)
 return node.children
 
 def document_lexers_overview(self):
 """Generate a tabular overview of all lexers.
 
 The columns are the lexer name, the extensions handled by this lexer
 (or "None"), the aliases and a link to the lexer class."""
 from pip._vendor.pygments.lexers._mapping import LEXERS
 from pip._vendor.pygments.lexers import find_lexer_class
 out = []
 
 table = []
 
 def format_link(name, url):
 if url:
 return f'`{name} <{url}>`_'
 return name
 
 for classname, data in sorted(LEXERS.items(), key=lambda x: x[1][1].lower()):
 lexer_cls = find_lexer_class(data[1])
 extensions = lexer_cls.filenames + lexer_cls.alias_filenames
 
 table.append({
 'name': format_link(data[1], lexer_cls.url),
 'extensions': ', '.join(extensions).replace('*', '\\*').replace('_', '\\') or 'None',
 'aliases': ', '.join(data[2]),
 'class': f'{data[0]}.{classname}'
 })
 
 column_names = ['name', 'extensions', 'aliases', 'class']
 column_lengths = [max([len(row[column]) for row in table if row[column]])
 for column in column_names]
 
 def write_row(*columns):
 """Format a table row"""
 out = []
 for length, col in zip(column_lengths, columns):
 if col:
 out.append(col.ljust(length))
 else:
 out.append(' '*length)
 
 return ' '.join(out)
 
 def write_seperator():
 """Write a table separator row"""
 sep = ['='*c for c in column_lengths]
 return write_row(*sep)
 
 out.append(write_seperator())
 out.append(write_row('Name', 'Extension(s)', 'Short name(s)', 'Lexer class'))
 out.append(write_seperator())
 for row in table:
 out.append(write_row(
 row['name'],
 row['extensions'],
 row['aliases'],
 f':class:`~{row["class"]}`'))
 out.append(write_seperator())
 
 return '\n'.join(out)
 
 def document_lexers(self):
 from pip._vendor.pygments.lexers._mapping import LEXERS
 from pip._vendor import pygments
 import inspect
 import pathlib
 
 out = []
 modules = {}
 moduledocstrings = {}
 for classname, data in sorted(LEXERS.items(), key=lambda x: x[0]):
 module = data[0]
 mod = __import__(module, None, None, [classname])
 self.filenames.add(mod.__file__)
 cls = getattr(mod, classname)
 if not cls.__doc__:
 print(f"Warning: {classname} does not have a docstring.")
 docstring = cls.__doc__
 if isinstance(docstring, bytes):
 docstring = docstring.decode('utf8')
 
 example_file = getattr(cls, '_example', None)
 if example_file:
 p = pathlib.Path(inspect.getabsfile(pygments)).parent.parent /\
 'tests' / 'examplefiles' / example_file
 content = p.read_text(encoding='utf-8')
 if not content:
 raise Exception(
 f"Empty example file '{example_file}' for lexer "
 f"{classname}")
 
 if data[2]:
 lexer_name = data[2][0]
 docstring += '\n\n    .. admonition:: Example\n'
 docstring += f'\n      .. code-block:: {lexer_name}\n\n'
 for line in content.splitlines():
 docstring += f'          {line}\n'
 
 if cls.version_added:
 version_line = f'.. versionadded:: {cls.version_added}'
 else:
 version_line = ''
 
 modules.setdefault(module, []).append((
 classname,
 ', '.join(data[2]) or 'None',
 ', '.join(data[3]).replace('*', '\\*').replace('_', '\\') or 'None',
 ', '.join(data[4]) or 'None',
 docstring,
 version_line))
 if module not in moduledocstrings:
 moddoc = mod.__doc__
 if isinstance(moddoc, bytes):
 moddoc = moddoc.decode('utf8')
 moduledocstrings[module] = moddoc
 
 for module, lexers in sorted(modules.items(), key=lambda x: x[0]):
 if moduledocstrings[module] is None:
 raise Exception(f"Missing docstring for {module}")
 heading = moduledocstrings[module].splitlines()[4].strip().rstrip('.')
 out.append(MODULEDOC % (module, heading, '-'*len(heading)))
 for data in lexers:
 out.append(LEXERDOC % data)
 
 return ''.join(out)
 
 def document_formatters(self):
 from pip._vendor.pygments.formatters import FORMATTERS
 
 out = []
 for classname, data in sorted(FORMATTERS.items(), key=lambda x: x[0]):
 module = data[0]
 mod = __import__(module, None, None, [classname])
 self.filenames.add(mod.__file__)
 cls = getattr(mod, classname)
 docstring = cls.__doc__
 if isinstance(docstring, bytes):
 docstring = docstring.decode('utf8')
 heading = cls.__name__
 out.append(FMTERDOC % (heading, ', '.join(data[2]) or 'None',
 ', '.join(data[3]).replace('*', '\\*') or 'None',
 docstring))
 return ''.join(out)
 
 def document_filters(self):
 from pip._vendor.pygments.filters import FILTERS
 
 out = []
 for name, cls in FILTERS.items():
 self.filenames.add(sys.modules[cls.__module__].__file__)
 docstring = cls.__doc__
 if isinstance(docstring, bytes):
 docstring = docstring.decode('utf8')
 out.append(FILTERDOC % (cls.__name__, name, docstring))
 return ''.join(out)
 
 
 def setup(app):
 app.add_directive('pygmentsdoc', PygmentsDoc)
 
 |