21

I use Sphinx to make a website that contains code samples. I'm successful using the .. code-block directive to get syntax highlighting. But I can't get inline syntax highlighting using this code:

.. role:: bash(code)
   :language: bash

Test inline: :bash:`export FOO="bar"`.

.. code-block:: bash

    export FOO="bar"

which produces this output i.e. inline code not highlighted while block code is:

result

Problem to me is that the generated HTML for inline code contains long class names while it does not for code blocks. Here is the output HTML (indented for readability):

<p>Test inline:
    <tt class="code bash docutils literal">
        <span class="name builtin">
            <span class="pre">export</span>
        </span>
        <span class="name variable">
            <span class="pre">FOO</span>
        </span>
        <span class="operator">
            <span class="pre">=</span>
        </span>
        <span class="literal string double">
            <span class="pre">&quot;bar&quot;</span>
        </span>
    </tt>.
</p>


<p>Test code-block:</p>
<div class="highlight-bash">
    <div class="highlight">
        <pre>
            <span class="nb">export </span>
            <span class="nv">FOO</span>
            <span class="o">=</span>
            <span class="s2">&quot;bar&quot;</span>
        </pre>
    </div>
</div>

Any help would be very much appreciated.

imz -- Ivan Zakharyaschev
  • 4,921
  • 6
  • 53
  • 104

6 Answers6

19

syntax_highlight is an ordinary docutils setting, which can be set in docutils.conf. This file is respected by Sphinx too, if placed in the Sphinx's configuration directory (where conf.py resides):

[restructuredtext parser]
syntax_highlight = short

This is much better than patching docutils or sphinx code or creating a long name CSS file.

Sebastian Schrader
  • 1,453
  • 15
  • 19
  • This should be the accepted answer, by far the cleanest solution. – Brian Jul 29 '18 at 20:53
  • 1
    This fixed the class names but highlighting didn't come through unless I also added `:class: highlight` as an option on the `.. role` directive, as shown in Adobe's answer. – goodmami Nov 24 '20 at 01:03
5

Found a better (sphinx-only) solution: in sphinx/builders/html.py find a line

from docutils.core import Publisher

and change it to:

from docutils.core import Publisher
def process_programmatic_settings(self, settings_spec,
                                  settings_overrides,
                                  config_section):
    if self.settings is None:
        defaults = (settings_overrides or {}).copy()
        # Propagate exceptions by default when used programmatically:
        defaults.setdefault('traceback', True)
        defaults.setdefault('syntax_highlight', 'short') # ADDED THIS LINE
        self.get_settings(settings_spec=settings_spec,
                          config_section=config_section,
                          **defaults)
Publisher.process_programmatic_settings = process_programmatic_settings

This solution is better then previous ones: since it doesn't double the amount of css rules, and doesn't modify docutils.

Still, ideal solution would only change conf.py. So there's a plenty space for improvement.

Adobe
  • 12,967
  • 10
  • 85
  • 126
  • The docutils solution is nice and quite well documented. Still, as far as I know, it can't be applied easily to sphinx. So, a sphinx compatible solution would still imply a double amount of css if I get things correctly. –  Feb 14 '14 at 01:22
  • @binoua: what do You mean? Each of the three answers I posted solves the problem for sphinx. One of them solves it by providing the `pygments.css`, another modifies default option on docutils, finally this one modifies sphinx. – Adobe Feb 14 '14 at 05:58
  • thanks for your answer. The pygments.css method is basically the [solution I came up to](http://stackoverflow.com/a/21601271/378147) i.e. providing a css with both short and long names. Correct me if I'm wrong but the two other solutions imply directly modifying the package (docutils in one case, sphinx in the other) which I agree is a solution too but, no offense, not very satisfying since this patch should be applied after each package update while not fixed directly by docutils/sphinx developer team. –  Feb 14 '14 at 09:18
  • @binoua: as I understand You generate pygments.css every time You compile sphinx. That's no problem with my first answer I just wanted to note that You can just add the file to the theme. You solution doubles the amount of css rules needed to color the code, which will result in slower rendering. If that's ok for You - no problem. Both solutions requiring modifications are small and easy: there's nothing religious about the code of sphinx or docutils - one can tweak it, and tweak again after each update. Finally - one can implement the sphinx solution as an extension (custom html builder). – Adobe Feb 14 '14 at 13:01
  • You're totally right: the custom html builder can definitively be a sphinx extension (I didn't thought about that). That would be the proper solution in my opinion. For the archive, I actually generate pygment.css once thanks to the script above. –  Feb 14 '14 at 16:50
3

OK I used this workaround: I generate a css file that contains both short and long names. I'm still interested in the "good" answer.

#!/usr/bin/env python

"""Generate a css file thanks to pygments that will contain both short
and long class names."""


import subprocess
import sys


PYGMENTIZE = 'pygmentize'


def parse_command_line():
    import argparse
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('-s', '--style', default='colorful')
    parser.add_argument('-p', '--prefix', default='.highlight')
    return parser.parse_args()


def pygmentize(style, prefix='.highlight'):
    cmd = '{0} -f html -S {1} -a {2}'.format(PYGMENTIZE, style, prefix)
    # This will fail if pygmentize does not exist.
    try:
        p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
    except OSError:
        print >> sys.stderr, '{0}: command not found'.format(PYGMENTIZE)
        exit(1)

    out, err = p.communicate()
    if p.returncode != 0:
        exit(p.returncode)
    return out


def main():
    args = parse_command_line()
    style = args.style
    prefix = args.prefix

    # Print new css header.
    header = """\
/* 
 * This is pygment css style {0} generated with
 *     {1}
 */""".format(style, ' '.join(sys.argv))
    print header

    # Parse pygmentize output.
    # Find long names based on comments.
    content = pygmentize(style, prefix)
    s = content.splitlines()
    out = ''
    for line in s:
        start = line.find("/* ") + 3
        end = line.find(" */")
        # if line has a comment
        if start != 2:
            comment = line[start:end]
            name = '.' + comment.lower()
            arg = line[line.find('{ '): start - 4]
            out += '%(prefix)s %(name)s %(arg)s\n' % vars()

    print content
    print out


if __name__ == '__main__':
    main()
2

When a sphinx theme has static/pygments.css -- then the file is not overwritten. So I just keep the file which contains both short and long names (which I obtained using regexp in emacs):

.highlight .hll { background-color: #ffffcc }
/* .highlight  { background: #eeffcc; } */
.highlight .c { color: #408090; font-style: italic } /* comment */
.highlight .comment { color: #408090; font-style: italic }
.highlight .err { border: 1px solid #ff0000 } /* error */
.highlight .error { border: 1px solid #ff0000 }
.highlight .k { color: #007020; font-weight: bold } /* keyword */
.highlight .keyword { color: #007020; font-weight: bold }
.highlight .o { color: #666666 } /* operator */
.highlight .operator { color: #666666 }
.highlight .cm { color: #408090; font-style: italic } /* comment.multiline */
.highlight .comment.multiline { color: #408090; font-style: italic }
.highlight .cp { color: #007020 } /* comment.preproc */
.highlight .comment.preproc { color: #007020 }
.highlight .c1 { color: #408090; font-style: italic } /* comment.single */
.highlight .comment.single { color: #408090; font-style: italic }
.highlight .cs { color: #408090; background-color: #fff0f0 } /* comment.special */
.highlight .comment.special { color: #408090; background-color: #fff0f0 }
.highlight .gd { color: #a00000 } /* generic.deleted */
.highlight .generic.deleted { color: #a00000 }
.highlight .ge { font-style: italic } /* generic.emph */
.highlight .generic.emph { font-style: italic }
.highlight .gr { color: #ff0000 } /* generic.error */
.highlight .generic.error { color: #ff0000 }
.highlight .gh { color: #000080; font-weight: bold } /* generic.heading */
.highlight .generic.heading { color: #000080; font-weight: bold }
.highlight .gi { color: #00a000 } /* generic.inserted */
.highlight .generic.inserted { color: #00a000 }
.highlight .go { color: #333333 } /* generic.output */
.highlight .generic.output { color: #333333 }
.highlight .gp { color: #c65d09; font-weight: bold } /* generic.prompt */
.highlight .generic.prompt { color: #c65d09; font-weight: bold }
.highlight .gs { font-weight: bold } /* generic.strong */
.highlight .generic.strong { font-weight: bold }
.highlight .gu { color: #800080; font-weight: bold } /* generic.subheading */
.highlight .generic.subheading { color: #800080; font-weight: bold }
.highlight .gt { color: #0044dd } /* generic.traceback */
.highlight .generic.traceback { color: #0044dd }
.highlight .kc { color: #007020; font-weight: bold } /* keyword.constant */
.highlight .keyword.constant { color: #007020; font-weight: bold }
.highlight .kd { color: #007020; font-weight: bold } /* keyword.declaration */
.highlight .keyword.declaration { color: #007020; font-weight: bold }
.highlight .kn { color: #007020; font-weight: bold } /* keyword.namespace */
.highlight .keyword.namespace { color: #007020; font-weight: bold }
.highlight .kp { color: #007020 } /* keyword.pseudo */
.highlight .keyword.pseudo { color: #007020 }
.highlight .kr { color: #007020; font-weight: bold } /* keyword.reserved */
.highlight .keyword.reserved { color: #007020; font-weight: bold }
.highlight .kt { color: #902000 } /* keyword.type */
.highlight .keyword.type { color: #902000 }
.highlight .m { color: #208050 } /* literal.number */
.highlight .literal.number { color: #208050 }
.highlight .s { color: #4070a0 } /* literal.string */
.highlight .literal.string { color: #4070a0 }
.highlight .na { color: #4070a0 } /* name.attribute */
.highlight .name.attribute { color: #4070a0 }
.highlight .nb { color: #007020 } /* name.builtin */
.highlight .name.builtin { color: #007020 }
.highlight .nc { color: #0e84b5; font-weight: bold } /* name.class */
.highlight .name.class { color: #0e84b5; font-weight: bold }
.highlight .no { color: #60add5 } /* name.constant */
.highlight .name.constant { color: #60add5 }
.highlight .nd { color: #555555; font-weight: bold } /* name.decorator */
.highlight .name.decorator { color: #555555; font-weight: bold }
.highlight .ni { color: #d55537; font-weight: bold } /* name.entity */
.highlight .name.entity { color: #d55537; font-weight: bold }
.highlight .ne { color: #007020 } /* name.exception */
.highlight .name.exception { color: #007020 }
.highlight .nf { color: #06287e } /* name.function */
.highlight .name.function { color: #06287e }
.highlight .nl { color: #002070; font-weight: bold } /* name.label */
.highlight .name.label { color: #002070; font-weight: bold }
.highlight .nn { color: #0e84b5; font-weight: bold } /* name.namespace */
.highlight .name.namespace { color: #0e84b5; font-weight: bold }
.highlight .nt { color: #062873; font-weight: bold } /* name.tag */
.highlight .name.tag { color: #062873; font-weight: bold }
.highlight .nv { color: #bb60d5 } /* name.variable */
.highlight .name.variable { color: #bb60d5 }
.highlight .ow { color: #007020; font-weight: bold } /* operator.word */
.highlight .operator.word { color: #007020; font-weight: bold }
.highlight .w { color: #bbbbbb } /* text.whitespace */
.highlight .text.whitespace { color: #bbbbbb }
.highlight .mf { color: #208050 } /* literal.number.float */
.highlight .literal.number.float { color: #208050 }
.highlight .mh { color: #208050 } /* literal.number.hex */
.highlight .literal.number.hex { color: #208050 }
.highlight .mi { color: #208050 } /* literal.number.integer */
.highlight .literal.number.integer { color: #208050 }
.highlight .mo { color: #208050 } /* literal.number.oct */
.highlight .literal.number.oct { color: #208050 }
.highlight .sb { color: #4070a0 } /* literal.string.backtick */
.highlight .literal.string.backtick { color: #4070a0 }
.highlight .sc { color: #4070a0 } /* literal.string.char */
.highlight .literal.string.char { color: #4070a0 }
.highlight .sd { color: #4070a0; font-style: italic } /* literal.string.doc */
.highlight .literal.string.doc { color: #4070a0; font-style: italic }
.highlight .s2 { color: #4070a0 } /* literal.string.double */
.highlight .literal.string.double { color: #4070a0 }
.highlight .se { color: #4070a0; font-weight: bold } /* literal.string.escape */
.highlight .literal.string.escape { color: #4070a0; font-weight: bold }
.highlight .sh { color: #4070a0 } /* literal.string.heredoc */
.highlight .literal.string.heredoc { color: #4070a0 }
.highlight .si { color: #70a0d0; font-style: italic } /* literal.string.interpol */
.highlight .literal.string.interpol { color: #70a0d0; font-style: italic }
.highlight .sx { color: #c65d09 } /* literal.string.other */
.highlight .literal.string.other { color: #c65d09 }
.highlight .sr { color: #235388 } /* literal.string.regex */
.highlight .literal.string.regex { color: #235388 }
.highlight .s1 { color: #4070a0 } /* literal.string.single */
.highlight .literal.string.single { color: #4070a0 }
.highlight .ss { color: #517918 } /* literal.string.symbol */
.highlight .literal.string.symbol { color: #517918 }
.highlight .bp { color: #007020 } /* name.builtin.pseudo */
.highlight .name.builtin.pseudo { color: #007020 }
.highlight .vc { color: #bb60d5 } /* name.variable.class */
.highlight .name.variable.class { color: #bb60d5 }
.highlight .vg { color: #bb60d5 } /* name.variable.global */
.highlight .name.variable.global { color: #bb60d5 }
.highlight .vi { color: #bb60d5 } /* name.variable.instance */
.highlight .name.variable.instance { color: #bb60d5 }
.highlight .il { color: #208050 } /* literal.number.integer.long */
.highlight .literal.number.integer.long { color: #208050 }

I run into another issue -- I'm using bootstrap theme, and it defines label too...

Adobe
  • 12,967
  • 10
  • 85
  • 126
2

Here's a better than previous workaround:

Change default --syntax-highlight option of docuitls. In

docutils/parsers/rst/__init__.py

Find

         ('Token name set for parsing code with Pygments: one of '
          '"long", "short", or "none (no parsing)". Default is "short".',
          ['--syntax-highlight'],
          {'choices': ['long', 'short', 'none'],
           'default': 'long', 'metavar': '<format>'}),

and chage the default to short:

           'default': 'short', 'metavar': '<format>'}),

The method to highlight inline code in sphnix is to register a new role:

.. role:: py(code)
   :language: py
   :class: highlight

This construct is from docutils, not from sphinx. Then one can change the docutils's default, to get the desired output (short classes). A better solution would be to set the value during the init of the class in sphinx. But where that happens might not be easy to find.

This solution is much better than the previous since it doesn't double the number of css rules to match to color the code.

Community
  • 1
  • 1
Adobe
  • 12,967
  • 10
  • 85
  • 126
  • The docutils solution is nice and quite well documented. Still, as far as I know, it can't be applied easily to sphinx. So, a sphinx compatible solution would still imply a double amount of css if I get things right. –  Feb 14 '14 at 01:42
2

This can be fixed by adding 'sphinxcontrib.inlinesyntaxhighlight' extension in conf.py:

extensions = [ 'sphinxcontrib.inlinesyntaxhighlight' ]

# use language set by highlight directive if no language is set by role
inline_highlight_respect_highlight = False

# use language set by highlight directive if no role is set
inline_highlight_literals = False

Extension documentation

The extension is available here and on PyPi.

timgeb
  • 76,762
  • 20
  • 123
  • 145
Donatello
  • 3,486
  • 3
  • 32
  • 38