[Doc-SIG] code-block directive

Gael Varoquaux gael.varoquaux at normalesup.org
Mon Nov 13 02:23:15 CET 2006


Hello,

I have added python-code syntax-highlighting. This feature has been hand
coded in extensions like the trac extension, or in rest2web. This leads
to variants of rest that are not compatible with docutils. I have thus
added a crude version of syntax-highlighting. The new directive follows
the trac directive:

.. code-block:: python

I have not coded support for other language than python, and the
directive default to a literal block if the language isn't python.
For the latex2e writer, where I was able to use the listings package, and
thus get free support for other languages. For the newlatex2e it is
impossible to use the listings package, as the code cannot be passed as
macro arguments (see section 5.1 of the listings manual).

This is a first draft and is probably of a lower quality then other
directive. I would like to push for inclusion of this new directive even
if it doesn't support all the features it could to regain language
compatibility with trac.

Comments on the patch are welcome.

Regards,

Gaël

-------------- next part --------------
Index: docutils/docutils/parsers/rst/directives/body.py
===================================================================
--- docutils/docutils/parsers/rst/directives/body.py	(revision 4799)
+++ docutils/docutils/parsers/rst/directives/body.py	(working copy)
@@ -108,7 +108,21 @@
         node.line = self.content_offset + 1
         return [node] + messages
 
+class CodeBlock(Directive):
 
+    required_arguments = 1
+    optional_arguments = 0
+    has_content = True
+
+    def run(self):
+        self.assert_has_content()
+        text = '\n'.join(self.content)
+        self.options['language'] = self.arguments[0]
+        node = nodes.code_block(text, text, **self.options)
+        node.line = self.content_offset + 1
+        return [node]
+
+
 class Rubric(Directive):
 
     required_arguments = 1
Index: docutils/docutils/parsers/rst/directives/__init__.py
===================================================================
--- docutils/docutils/parsers/rst/directives/__init__.py	(revision 4799)
+++ docutils/docutils/parsers/rst/directives/__init__.py	(working copy)
@@ -29,6 +29,7 @@
       'topic': ('body', 'Topic'),
       'line-block': ('body', 'LineBlock'),
       'parsed-literal': ('body', 'ParsedLiteral'),
+      'code-block': ('body', 'CodeBlock'),
       'rubric': ('body', 'Rubric'),
       'epigraph': ('body', 'Epigraph'),
       'highlights': ('body', 'Highlights'),
Index: docutils/docutils/writers/html4css1/__init__.py
===================================================================
--- docutils/docutils/writers/html4css1/__init__.py	(revision 4799)
+++ docutils/docutils/writers/html4css1/__init__.py	(working copy)
@@ -21,6 +21,8 @@
 import time
 import re
 from types import ListType
+import token, tokenize
+import cStringIO
 try:
     import Image                        # check for the Python Imaging Library
 except ImportError:
@@ -28,8 +30,8 @@
 import docutils
 from docutils import frontend, nodes, utils, writers, languages
 from docutils.transforms import writer_aux
+from docutils.colorizer import python2html
 
-
 class Writer(writers.Writer):
 
     supported = ('html', 'html4css1', 'xhtml')
@@ -1075,6 +1077,18 @@
         # Content already processed:
         raise nodes.SkipNode
 
+    def visit_code_block(self, node):
+        if node['language'] == 'python':
+            self.body.append(python2html(node.astext().encode('ascii')))
+        else:
+            self.body.append('<div class="pysrc">')
+            self.body.append(node.astext().encode('ascii'))
+            self.body.append('</div>\n')
+        raise nodes.SkipNode
+
+    def depart_code_block(self, node):
+        pass
+
     def visit_literal_block(self, node):
         self.body.append(self.starttag(node, 'pre', CLASS='literal-block'))
 
Index: docutils/docutils/writers/html4css1/html4css1.css
===================================================================
--- docutils/docutils/writers/html4css1/html4css1.css	(revision 4799)
+++ docutils/docutils/writers/html4css1/html4css1.css	(working copy)
@@ -273,3 +273,29 @@
 
 ul.auto-toc {
   list-style-type: none }
+
+/* Python syntax highlighting */
+.pysrc {
+    font-weight: normal;
+    background-color: #eef2f7;
+    background-position:  right;
+    background-repeat: repeat-y;
+    border: 1px solid;
+    border-color: #999999;
+    margin: 20px;
+    padding:10px 10px 10px 20px;
+    font-size: smaller;
+    white-space: pre ;
+}
+
+.pykeyword {
+    font-weight: bold;
+    color: #262668 ;
+}
+.pycomment { color: #007600; }
+.pystring { color: #0000bb; }
+.pynumber { color:purple; }
+.pyoperator { color:purple; font-weight: bold; }
+.pytext { color:black; }
+.pyerror { font-weight: bold; color: red; }
+
Index: docutils/docutils/writers/latex2e/__init__.py
===================================================================
--- docutils/docutils/writers/latex2e/__init__.py	(revision 4799)
+++ docutils/docutils/writers/latex2e/__init__.py	(working copy)
@@ -639,6 +639,13 @@
               '\\usepackage{amsmath}\n',   # what fore amsmath.
               self.graphicx_package,
               '\\usepackage{color}\n',
+              '\\definecolor{darkgreen}{cmyk}{0.7, 0, 1, 0.5}\n',
+              '\\definecolor{darkblue}{cmyk}{1, 0.8, 0, 0}\n',
+              '\\definecolor{lightblue}{cmyk}{0.05,0,0,0.05}\n',
+              '\\definecolor{grey}{cmyk}{0.1,0.1,0.1,1}\n',
+              '\\definecolor{lightgrey}{cmyk}{0,0,0,0.5}\n',
+              '\\definecolor{purple}{cmyk}{0.8,1,0,0}\n',
+              '\\usepackage{listings}\n\\def\\codeblocksize{\\small}\n',
               '\\usepackage{multirow}\n',
               '\\usepackage{ifthen}\n',   # before hyperref!
               self.linking % (self.colorlinks, self.hyperlink_color, self.hyperlink_color),
@@ -1052,6 +1059,40 @@
     def depart_classifier(self, node):
         self.body.append( '})\n' )
 
+    def visit_code_block(self, node):
+        self.literal = 1
+        self.body.append(r"""
+{\codeblocksize
+\lstset{language=%s,
+    extendedchars=true,
+    basicstyle=\ttfamily,
+    keywordstyle=\sffamily\bfseries,
+    identifierstyle=\sffamily,
+    commentstyle=\slshape\color{darkgreen},
+    stringstyle=\rmfamily\color{blue},
+    showstringspaces=false,
+    tabsize=4,
+    breaklines=true,
+    classoffset=1,
+    otherkeywords={[,],=,:},
+    keywordstyle=\color{purple}\bfseries,
+    classoffset=0
+    escapebegin={\color{darkgreen}},
+    backgroundcolor=\color{lightblue},
+    fillcolor=\color{lightblue},
+    xleftmargin=0pt,
+    fillcolor=\color{white},
+    frame=single,
+    fillcolor=\color{lightblue},
+    rulecolor=\color{lightgrey},
+    basicstyle=\ttfamily\codeblocksize} 
+\begin{lstlisting}{language=%s}
+""" %( node['language'], node['language']) )
+
+    def depart_code_block(self, node):
+        self.body.append('\n\\end{lstlisting}}\n')
+        self.literal = 0
+
     def visit_colspec(self, node):
         self.active_table.visit_colspec(node)
 
Index: docutils/docutils/writers/newlatex2e/__init__.py
===================================================================
--- docutils/docutils/writers/newlatex2e/__init__.py	(revision 4799)
+++ docutils/docutils/writers/newlatex2e/__init__.py	(working copy)
@@ -22,6 +22,7 @@
 from docutils.writers.newlatex2e import unicode_map
 from docutils.transforms import writer_aux
 
+from docutils.colorizer import python2latex
 
 class Writer(writers.Writer):
 
@@ -402,6 +403,18 @@
     visit_doctest_block = visit_literal_block
     depart_doctest_block = depart_literal_block
 
+    def visit_code_block(self, node):
+        if node['language'] == 'python':
+            self.body.append(python2latex(node.astext().encode('ascii')))
+        else:
+            self.body.append('\n\\begin{verbatim}\n')
+            self.body.append(node.astext().encode('ascii'))
+            self.body.append('\n\\end{verbatim}\n')
+        raise nodes.SkipChildren 
+
+    def depart_code_block(self, node):
+        pass
+
     inline_literal = 0
 
     def visit_literal(self, node):
Index: docutils/docutils/writers/newlatex2e/base.tex
===================================================================
--- docutils/docutils/writers/newlatex2e/base.tex	(revision 4799)
+++ docutils/docutils/writers/newlatex2e/base.tex	(working copy)
@@ -79,6 +79,12 @@
   \usepackage[colorlinks=false,pdfborder={0 0 0}]{hyperref}
   % Get color, e.g. for links and system messages.
   \usepackage{color}
+  \definecolor{darkgreen}{cmyk}{0.7, 0, 1, 0.5}
+  \definecolor{darkblue}{cmyk}{1, 0.8, 0, 0}
+  \definecolor{lightblue}{cmyk}{0.05,0,0,0.05}
+  \definecolor{grey}{cmyk}{0.1,0.1,0.1,1}
+  \definecolor{lightgrey}{cmyk}{0,0,0,0.5}
+  \definecolor{purple}{cmyk}{0.8,1,0,0}
   % Get \textnhtt macro (non-hyphenating type writer).
   \usepackage{hyphenat}
   % For sidebars.
@@ -113,6 +119,7 @@
   \renewcommand{\texteuro}{\euro}%
 }
 
+
 % Taken from
 % <http://groups.google.de/groups?selm=1i0n5tgtplti420e1omp4pctlv19jpuhbb%404ax.com>
 % and modified.  Used with permission.
@@ -413,6 +420,21 @@
   \csname D\DEVparent subtitle\endcsname{#1}%
 }
 
+\providecommand{\pynumber}[1]{\color{purple} #1}
+\providecommand{\pyoperator}[1]{{\bfseries\textcolor{purple}{#1}}}
+\providecommand{\pystring}[1]{{\rmfamily\textcolor{blue}{#1}}}
+\providecommand{\pycomment}[1]{{\slshape\textcolor{darkgreen}{#1}}}
+\providecommand{\pyerror}[1]{{\bfseries\textcolor{red}{#1}}}
+\providecommand{\pykeyword}[1]{{\sffamily\bfseries\textcolor{darkblue}{#1}}}
+\providecommand{\pytext}[1]{{\sffamily #1}}
+\providecommand{\DNcodeblock}[1]{%
+\fcolorbox{black}{lightblue}{%
+\begin{minipage}{\linewidth}%
+\small%
+#1
+\end{minipage}%
+}
+}
 \providecommand{\DNliteralblock}[1]{%
   \Dmakelistenvironment{}{%
     \ifthenelse{\equal{\Dinsidetabular}{true}}{%
Index: docutils/docutils/nodes.py
===================================================================
--- docutils/docutils/nodes.py	(revision 4799)
+++ docutils/docutils/nodes.py	(working copy)
@@ -1267,6 +1267,7 @@
 class option_string(Part, TextElement): pass
 class description(Part, Element): pass
 class literal_block(General, FixedTextElement): pass
+class code_block(General, FixedTextElement): pass
 class doctest_block(General, FixedTextElement): pass
 class line_block(General, Element): pass
 
@@ -1447,8 +1448,8 @@
     abbreviation acronym address admonition attention attribution author
         authors
     block_quote bullet_list
-    caption caution citation citation_reference classifier colspec comment
-        compound contact container copyright
+    caption caution citation citation_reference classifier code_blockcolspec 
+        comment compound contact container copyright
     danger date decoration definition definition_list definition_list_item
         description docinfo doctest_block document
     emphasis entry enumerated_list error


More information about the Doc-SIG mailing list