|
|
 |
|
Title: Syntax-highlighted code blocks for docutils
Submitter: Kevin Schluff
(other recipes)
Last Updated: 2006/03/04
Version no: 1.1
Category:
|
|
1 vote(s)
|
|
|
|
Description:
Code samples in reStructuredText documents are normally shown as plain literal blocks. This recipe uses the SilverCity ( http://silvercity.sourceforge.net/ ) lexing package to generate syntax highlighted code blocks instead.
Source: Text Source
import SilverCity
import docutils.parsers.rst
import StringIO
def code_block( name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine ):
"""
The code-block directive provides syntax highlighting for blocks
of code. It is used with the the following syntax::
.. code-block:: CPP
#include <iostream>
int main( int argc, char* argv[] )
{
std::cout << "Hello world" << std::endl;
}
The directive requires the name of a language supported by SilverCity
as its only argument. All code in the indented block following
the directive will be colourized. Note that this directive is only
supported for HTML writers.
"""
language = arguments[0]
try:
module = getattr(SilverCity, language)
generator = getattr(module, language+"HTMLGenerator")
except AttributeError:
error = state_machine.reporter.error( "No SilverCity lexer found "
"for language '%s'." % language,
docutils.nodes.literal_block(block_text, block_text), line=lineno )
return [error]
io = StringIO.StringIO()
generator().generate_html( io, '\n'.join(content) )
html = '<div class="code-block">\n%s\n</div>\n' % io.getvalue()
raw = docutils.nodes.raw('',html, format = 'html')
return [raw]
code_block.arguments = (1,0,0)
code_block.options = {'language' : docutils.parsers.rst.directives.unchanged }
code_block.content = 1
docutils.parsers.rst.directives.register_directive( 'code-block', code_block )
if __name__ == "__main__":
import docutils.core
docutils.core.publish_cmdline(writer_name='html')
Discussion:
This module adds a new directive, "code-block", to docutils. The directive can be used wherever you otherwise might use a literal block. For example:
.. code-block:: Python
def hello( name ):
print "Hello,",name
instead of,
::
def hello( name ):
print "Hello,",name
Displaying the HTML output requires a stylesheet that merges the 'default.css' from docutils, the 'default.css' from SilverCity and the following style:
div.code-block{
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee;
font-family: "Courier New", Courier, monospace;
font-size: 10pt;
}
The syntax-highlighted text is inserted into the docutils tree as a raw-node with HTML format. So, it will only be displayed when generating HTML documents, and will be ignored by other writers (e.g., LaTex)
You can run this as a standalone script to generate HTML, or import it into other modules that invoke the docutils HTML writer. Simply importing the module registers the code-block directive and makes it available.
|
|
Add comment
|
|
Number of comments: 4
Small feature addition, Michael Lerner, 2005/04/01
I like this a lot, but I want it to stay up-to-date with the current source. I changed the recipie a bit so that you can specify a source-file instead of including source directly.
#!/usr/bin/env python
import SilverCity
import docutils.parsers.rst
import StringIO
def code_block( name, arguments, options, content, lineno,
content_offset, block_text, state, state_machine ):
"""
The code-block directive provides syntax highlighting for blocks
of code. It is used with the the following syntax::
.. code-block:: CPP
#include <iostream>
int main( int argc, char* argv[] )
{
std::cout << "Hello world" << std::endl;
}
The directive requires the name of a language supported by SilverCity
as its only argument. All code in the indented block following
the directive will be colourized. Note that this directive is only
supported for HTML writers.
The directive can also be told to include a source file directly::
.. code-block::
:language: Python
:source-file: ../myfile.py
You cannot both specify a source-file and include code directly.
"""
try:
language = arguments[0]
except IndexError:
language = options['language']
if content and 'source-file' in options:
error = state_machine.reporter.error( "You cannot both specify a source-file and include code directly.",
docutils.nodes.literal_block(block_text,block_text), line=lineno)
return [error]
if not content:
try:
content = [line.rstrip() for line in file(options['source-file'])]
except KeyError:
# source-file was not specified
pass
try:
module = getattr(SilverCity, language)
generator = getattr(module, language+"HTMLGenerator")
except AttributeError:
error = state_machine.reporter.error( "No SilverCity lexer found "
"for language '%s'." % language,
docutils.nodes.literal_block(block_text, block_text), line=lineno )
return [error]
io = StringIO.StringIO()
generator().generate_html( io, '\n'.join(content) )
html = '<div class="code-block">\n%s\n</div>\n' % io.getvalue()
raw = docutils.nodes.raw('',html, format = 'html')
return [raw]
#code_block.arguments = (1,0,0)
code_block.arguments = (0,2,1)
code_block.options = {'language' : docutils.parsers.rst.directives.unchanged,
'source-file' : docutils.parsers.rst.directives.path,}
code_block.content = 1
# Simply importing this module will make the directive available.
docutils.parsers.rst.directives.register_directive( 'code-block', code_block )
if __name__ == "__main__":
import docutils.core
docutils.core.publish_cmdline(writer_name='html')
This also lets you specify language directly as an option.
Add comment
oops, Michael Lerner, 2005/04/01
small correction:
if not content:
try:
content = [line.rstrip() for line in file(options['source-file'])]
except KeyError:
# source-file was not specified
pass
should be changed to
if not content:
try:
content = [line.rstrip() for line in file(options['source-file'])]
except KeyError:
# source-file was not specified
pass
except IOError:
error = state_machine.reporter.error( "Could not read file %s."%options['source-file'],
docutils.nodes.literal_block(block_text,block_text), line=lineno)
return [error]
to be a little more graceful
Add comment
Alexander Belchenko, 2006/03/06
SilverCity won't work with unicode strings.
Add comment
Update of recipe for new style Docutils directive, Chris Jobling, 2006/09/14
When I tried to reuse this recipe in the latest SVN snapshot of Docutils (0.5
[repository]), I discovered that the rst directive code has changed from a
functional interface to a class-based interface.
After a bit of fiddling I was able to get it to work. Here is the revised
code.
#!/usr/bin/env python
import SilverCity
from docutils import io, nodes, statemachine, utils
from docutils.parsers.rst import Directive, directives
import StringIO
# Modified to new class-based form by C.P. Jobling (C.P.Jobling@Swansea.ac.uk)
class CodeBlock(Directive):
"""
The code-block directive provides syntax highlighting for blocks
of code. It is used with the the following syntax::
.. code-block:: CPP
#include <iostream>
int main( int argc, char* argv[] )
{
std::cout << "Hello world" << std::endl;
}
The directive requires the name of a language supported by SilverCity
as its only argument. All code in the indented block following
the directive will be colourized. Note that this directive is only
supported for HTML writers.
The directive can also be told to include a source file directly::
.. code-block::
:language: Python
:source-file: ../myfile.py
You cannot both specify a source-file and include code directly.
"""
final_argument_whitespace = True
option_spec = {''language'' : directives.unchanged,
''source-file'' : directives.path,}
has_content = True
def run(self):
try:
language = self.arguments[0]
except IndexError:
language = self.options[''language'']
if self.content and ''source-file'' in self.options:
error = self.state_machine.reporter.error(
"You cannot both specify a source-file and include code directly.",
nodes.literal_block(block_text,block_text), line=lineno)
return [error]
if not self.content:
try:
self.content = [line.rstrip() for line infile(self.options[''source-file''])]
except KeyError:
# source-file was not specified pass
except IOError:
error = self.state_machine.reporter.error(
"Could not read file %s." %self.options[''source-file''],
nodes.literal_block(block_text, block_text), line=self.lineno)
return [error]
try:
module = getattr(SilverCity, language)
generator = getattr(module, language+"HTMLGenerator")
except AttributeError:
error = self.state_machine.reporter.error( "No SilverCity lexer found "
"for language ''%s''." %language,
nodes.literal_block(block_text, block_text), line=self.lineno )
return [error]
io = StringIO.StringIO()
generator().generate_html( io, ''\n''.join(self.content) )
html = ''<div class="code-block">\n%s\n</div>\n'' % io.getvalue()
raw = nodes.raw('''',html, format = ''html'')
return [raw]
During my researches for this change I discovered that the original recipe has
been adapted by the TRAC developers (http://trac.edgewall.org/) where it is
used in both code pretty-printing and revision browsing and with an reST
directive in their wiki. Their solution is more sophisticated than the one
here because it makes use of GNU/Enscript to typeset languages other than
those supported by SilverCity. It would be nice if this code could be
backported into Docutils.
Another comment is that this recipe could usefully subclass the Raw directive
which provides support for inserting code from URLs as well as files. This
also provides support for specifying text encoding of the code (although from
the above comment, this would appear to be unsupported by SilverCity).
Unfortunately, this is beyond my current skills!
Add comment
|
|
|
|
|
 |
|