pythonpython-sphinxpygments

How to customize the code-block language of Sphinx?


Consider the below situation:

.. code-block:: my_lang

    ...

If I want to make my_lang as some stuff such as Python, how can I do it?


Solution

  • First, you need to create a script (_ext/mylanglexer.py), and then add on the extensions on the conf.py

    (_ext is a convention but not a must.)

    _ext/mylanglexer.py

    # _ext/mylanglexer.py
    
    from pygments.lexers import get_lexer_by_name  # refer LEXERS
    from pygments.lexers._mapping import LEXERS
    from pygments.lexers.python import PythonLexer
    
    
    def setup(app):
        # choose one, both ok
        app.add_lexer('my_lang', get_lexer_by_name('py'))
        # app.add_lexer('my_lang', PythonLexer)
    

    conf.py

    # conf.py
    
    extensions = [
        ...
        '_ext.mylanglexer',  # types.ModuleType, they are likely the_module = __import__('sphinx.ext.autodoc')
    ]
    

    at some tutorial the tell you better add sys.path.append(os.path.abspath("./_ext")) then extensions = ['mylanglexer']

    Anyway, as long as you know that is a module, all extensions should be able to be import ..., so if your module is not in the default path, of course, you must append.

    Now, it's working!


    How does it work

    see the pygements.lexers.init.py

    # pygements.lexers.__init__.py
    
    def get_lexer_by_name(_alias, **options):
        ...
    
        # lookup builtin lexers
        for module_name, name, aliases, _, _ in LEXERS.values():  # <-- Be focus on this line.
            if _alias.lower() in aliases:
                return _lexer_cache[name](**options)  # The class object (module_name+key_name), for example: pygments.lexers.python.PythonLexer(**options)
        # continue with lexers from setuptools entrypoints
        for cls in find_plugin_lexers():
    
            ...
            return cls(**options)
    
        raise ClassNotFound('no lexer for alias %r found' % _alias)
    

    In which LEXERS is some kind of the following stuff.

    # pygments.lexers._mapping.py
    
    LEXERS = {
        # key_name: module_name, name, aliases: Tuple[str], _, _
        ...
        'ObjectiveCLexer': ('pygments.lexers.objective', 'Objective-C', ('objective-c', 'objectivec', 'obj-c', 'objc'), ('*.m', '*.h'), ('text/x-objective-c',)),
        'ObjectiveCppLexer': ('pygments.lexers.objective', 'Objective-C++', ('objective-c++', 'objectivec++', 'obj-c++', 'objc++'), ('*.mm', '*.hh'), ('text/x-objective-c++',)),
        'ObjectiveJLexer': ('pygments.lexers.javascript', 'Objective-J', ('objective-j', 'objectivej', 'obj-j', 'objj'), ('*.j',), ('text/x-objective-j',)),
        'OcamlLexer': ('pygments.lexers.ml', 'OCaml', ('ocaml',), ('*.ml', '*.mli', '*.mll', '*.mly'), ('text/x-ocaml',)),
        'OctaveLexer': ('pygments.lexers.matlab', 'Octave', ('octave',), ('*.m',), ('text/octave',)),
        'JsonLexer': ('pygments.lexers.data', 'JSON', ('json',), ('*.json', 'Pipfile.lock'), ('application/json',)),
        'PythonLexer': ('pygments.lexers.python', 'Python', ('python', 'py', 'sage', 'python3', 'py3'), ('*.py', '*.pyw', '*.jy', '*.sage', '*.sc', 'SConstruct', 'SConscript', '*.bzl', 'BUCK', 'BUILD', 'BUILD.bazel', 'WORKSPACE', '*.tac'), ('text/x-python', 'application/x-python', 'text/x-python3', 'application/x-python3')),
        ...
    }
    

    There you know, your _alias in the aliases, then will work!

    How can I customize my style?

    you can copy and do some modify the Lexer, such as ObjectiveCLexer, JsonLexer ...

    finally app.add_lexer('my_lang', YourLexer) that's kind of the problem.