Changeset 42 for bitstructures

Show
Ignore:
Timestamp:
01/06/08 14:41:54 (12 months ago)
Author:
simon
Message:

implemented syntax highlighting for codeblocks

Location:
bitstructures/trunk
Files:
7 modified

Legend:

Unmodified
Added
Removed
  • bitstructures/trunk/README

    r25 r42  
    1313Django 0.96 
    1414Python Markdown 1.6a http://www.freewisdom.org/projects/python-markdown/ 
     15Pygments 0.9 http://pygments.org/ 
  • bitstructures/trunk/css/bitstructures.css

    r24 r42  
    120120        text-decoration: none; 
    121121} 
     122 
     123pre { 
     124        background-color: #def; 
     125} 
     126 
     127/* rules for Pygments */ 
     128 
     129.highlight .k, .highlight .nb, .highlight .nt { 
     130        color: #555; 
     131        font-weight: bold; 
     132} 
  • bitstructures/trunk/substructure/codeblocks.py

    r25 r42  
    1818            return self.text 
    1919 
     20def is_codeblock_header_field(line): 
     21    if line.is_codeblock_line() and get_field_name(line): 
     22        return True 
     23    else: 
     24        return False 
     25 
     26def get_field_name(header_line): 
     27    name_value = header_line.get_text_without_indentation() 
     28    index = name_value.find(':') 
     29    if index != -1: 
     30        return name_value[0:index].strip() 
     31    else: 
     32        return None 
     33 
     34def get_field_value(header_line): 
     35    name_value = header_line.get_text_without_indentation() 
     36    index = name_value.find(':') 
     37    if index != -1: 
     38        return name_value[index+1:].strip() 
     39    else: 
     40        return None 
     41 
    2042class MarkdownLineTokeniser: 
    2143    def __init__(self, text): 
    2244        self._lines = iter(text.splitlines(True)) 
    23         self._current_line = None 
     45        self.current_line = None 
    2446        self._at_end = False 
    25  
    26     def get_current_line(self): 
    27         return self._current_line 
    2847 
    2948    def is_at_end(self): 
     
    3251    def read_next(self): 
    3352        try: 
    34             self._current_line = MarkdownLine(self._lines.next()) 
     53            self.current_line = MarkdownLine(self._lines.next()) 
    3554        except StopIteration: 
    3655            self._at_end = True 
    37             self._current_line = None 
     56            self.current_line = None 
     57 
     58class Section: 
     59    def __init__(self): 
     60        self.lines = list() 
     61 
     62    def get_text(self): 
     63        return ''.join(line.text for line in self.lines) 
     64 
     65    def append(self, line): 
     66        self.lines.append(line) 
     67 
     68class Codeblock(Section): 
     69    def __init__(self): 
     70        Section.__init__(self) 
     71        self.content_type = None 
     72 
     73    def is_codeblock(self): 
     74        return True 
     75 
     76    def get_code(self): 
     77        return ''.join(line.get_text_without_indentation() for line in self.lines) 
     78 
     79class NonCodeblock(Section): 
     80    def is_codeblock(self): 
     81        return False 
    3882 
    3983class MarkdownCodeblocksParser: 
     
    4589        lines.read_next() 
    4690        while not lines.is_at_end(): 
    47             if lines.get_current_line().is_indented(): 
    48                 codeblock = self._get_codeblock_text(lines) 
     91            if lines.current_line.is_indented(): 
     92                codeblock = self._get_codeblock(lines) 
    4993                count += 1 
    5094                if count == n: 
     
    5397        return None 
    5498 
    55     def _get_codeblock_text(self, lines): 
    56         text = str() 
    57         while (not lines.is_at_end()) and lines.get_current_line().is_codeblock_line(): 
    58             text = text + lines.get_current_line().get_text_without_indentation() 
     99    def parse(self, text): 
     100        sections = list() 
     101        lines = MarkdownLineTokeniser(text) 
     102        lines.read_next() 
     103        while not lines.is_at_end(): 
     104            if lines.current_line.is_indented(): 
     105                sections.append(self._get_codeblock(lines)) 
     106            else: 
     107                sections.append(self._get_non_codeblock(lines)) 
     108        return sections 
     109 
     110    def _get_codeblock(self, lines): 
     111        codeblock = Codeblock() 
     112        if is_codeblock_header_field(lines.current_line): 
     113            if get_field_name(lines.current_line) == 'Content-Type': 
     114                codeblock.content_type = get_field_value(lines.current_line) 
    59115            lines.read_next() 
    60         return text 
     116        while (not lines.is_at_end()) and lines.current_line.is_codeblock_line(): 
     117            codeblock.append(lines.current_line) 
     118            lines.read_next() 
     119        return codeblock 
     120 
     121    def _get_non_codeblock(self, lines): 
     122        section = NonCodeblock() 
     123        while (not lines.is_at_end()) and (not lines.current_line.is_indented()): 
     124            section.append(lines.current_line) 
     125            lines.read_next() 
     126        return section 
  • bitstructures/trunk/substructure/templatetags/substructure_tags.py

    r15 r42  
    11from django import template 
    22from django.utils.tzinfo import LocalTimezone 
     3from markdown import markdown 
     4from pygments import highlight 
     5from pygments.lexers import get_lexer_for_mimetype 
     6from pygments.formatters import HtmlFormatter 
     7from bitstructures.substructure.codeblocks import MarkdownCodeblocksParser 
    38 
    49def rfc3339(dt): 
     
    914    return dt_out.isoformat() 
    1015 
     16def syntax_highlighted_markdown(markdown_text): 
     17    parser = MarkdownCodeblocksParser() 
     18    formatter = HtmlFormatter() 
     19    sections = parser.parse(markdown_text) 
     20    for_markdown = list() 
     21    html = list() 
     22    for section in sections: 
     23        if section.is_codeblock() and section.content_type: 
     24            # run Markdown on any text that isn't to be syntax highlighted 
     25            if for_markdown: 
     26                html.append(markdown(''.join(for_markdown))) 
     27                for_markdown = list() 
     28            # run Pygments on code to be syntax highlighted 
     29            html.append(highlight(section.get_code(), 
     30                get_lexer_for_mimetype(section.content_type), formatter)) 
     31        else: 
     32            for_markdown.append(section.get_text()) 
     33    # run Markdown on any remaining text that isn't to be syntax highlighted 
     34    if for_markdown: 
     35        html.append(markdown(''.join(for_markdown))) 
     36    return ''.join(html) 
     37 
    1138register = template.Library() 
    12 register.filter("rfc3339", rfc3339) 
     39register.filter('rfc3339', rfc3339) 
     40register.filter('syntax_highlighted_markdown', syntax_highlighted_markdown) 
  • bitstructures/trunk/substructure/tests.py

    r25 r42  
    1 from bitstructures.substructure.codeblocks import MarkdownLine, MarkdownCodeblocksParser 
     1from bitstructures.substructure.codeblocks import MarkdownLine, MarkdownCodeblocksParser, is_codeblock_header_field, get_field_name, get_field_value 
    22import unittest 
    33 
    44class MarkdownLineTest(unittest.TestCase): 
     5    def setUp(self): 
     6        self.empty = MarkdownLine('') 
     7        self.foo_bar = MarkdownLine('    foo:bar') 
     8        self.foo_bar_spaces = MarkdownLine('    foo : bar') 
     9        self.foo_bar_newline = MarkdownLine('    foo:bar\n') 
     10 
    511    def test_is_blank(self): 
    6         self.assert_(MarkdownLine('').is_blank()) 
     12        self.assert_(self.empty.is_blank()) 
    713        self.assert_(MarkdownLine(' ').is_blank()) 
    814        self.assert_(MarkdownLine('\t').is_blank()) 
     
    1420        self.assert_(MarkdownLine('    code').is_indented()) 
    1521        self.assert_(MarkdownLine('     code').is_indented()) 
     22 
     23    def test_codeblock_header_field(self): 
     24        self.assert_(not is_codeblock_header_field(self.empty)) 
     25        self.assert_(not is_codeblock_header_field(MarkdownLine('foo'))) 
     26        self.assert_(not is_codeblock_header_field(MarkdownLine('foo:bar'))) 
     27        self.assert_(not is_codeblock_header_field(MarkdownLine('    foo'))) 
     28        self.assert_(not is_codeblock_header_field(MarkdownLine('    :'))) 
     29        self.assert_(not is_codeblock_header_field(MarkdownLine('    :foo'))) 
     30        self.assert_(is_codeblock_header_field(self.foo_bar)) 
     31        self.assertEqual('foo', get_field_name(self.foo_bar)) 
     32        self.assertEqual('bar', get_field_value(self.foo_bar)) 
     33        self.assert_(is_codeblock_header_field(self.foo_bar_spaces)) 
     34        self.assertEqual('foo', get_field_name(self.foo_bar_spaces)) 
     35        self.assertEqual('bar', get_field_value(self.foo_bar_spaces)) 
     36        self.assert_(is_codeblock_header_field(self.foo_bar_newline)) 
     37        self.assertEqual('foo', get_field_name(self.foo_bar_newline)) 
     38        self.assertEqual('bar', get_field_value(self.foo_bar_newline)) 
    1639 
    1740class MarkdownCodeblocksParserTest(unittest.TestCase): 
     
    3356text 
    3457    code2''' 
     58        self.with_content_type = '''    Content-Type: text/x-python 
     59    line2''' 
    3560 
    3661    def test_get_codeblock_on_empty(self): 
     
    5277 
    5378    def test_get_codeblock(self): 
    54         self.assertEqual('foo', self.parser.get_codeblock('    foo', 1)) 
     79        self.assertEqual('    foo', self.parser.get_codeblock('    foo', 1).get_text()) 
     80        self.assertEqual('foo', self.parser.get_codeblock('    foo', 1).get_code()) 
     81        self.assertEqual(self.two_line, 
     82            self.parser.get_codeblock(self.two_line, 1).get_text()) 
    5583        expect_twoline = '''line1 
    5684line2''' 
    5785        self.assertEqual(expect_twoline, 
    58                          self.parser.get_codeblock(self.two_line, 1)) 
     86            self.parser.get_codeblock(self.two_line, 1).get_code()) 
     87        self.assertEqual('    code\n\n', 
     88            self.parser.get_codeblock(self.text_code_text, 1).get_text()) 
    5989        self.assertEqual('code\n\n', 
    60                          self.parser.get_codeblock(self.text_code_text, 1)) 
     90            self.parser.get_codeblock(self.text_code_text, 1).get_code()) 
     91        self.assertEqual('    code1\n\n', 
     92            self.parser.get_codeblock(self.code_text_code, 1).get_text()) 
    6193        self.assertEqual('code1\n\n', 
    62                          self.parser.get_codeblock(self.code_text_code, 1)) 
     94            self.parser.get_codeblock(self.code_text_code, 1).get_code()) 
     95        self.assertEqual('    code2\n', 
     96            self.parser.get_codeblock(self.code_text_code, 2).get_text()) 
    6397        self.assertEqual('code2\n', 
    64                          self.parser.get_codeblock(self.code_text_code, 2)) 
     98            self.parser.get_codeblock(self.code_text_code, 2).get_code()) 
     99        self.assertEqual('    code1\n', 
     100            self.parser.get_codeblock(self.code_text_code_no_blank_lines, 1).get_text()) 
    65101        self.assertEqual('code1\n', 
    66                          self.parser.get_codeblock(self.code_text_code_no_blank_lines, 1)) 
     102            self.parser.get_codeblock(self.code_text_code_no_blank_lines, 1).get_code()) 
     103        self.assertEqual('    code2', 
     104            self.parser.get_codeblock(self.code_text_code_no_blank_lines, 2).get_text()) 
    67105        self.assertEqual('code2', 
    68                          self.parser.get_codeblock(self.code_text_code_no_blank_lines, 2)) 
     106            self.parser.get_codeblock(self.code_text_code_no_blank_lines, 2).get_code()) 
     107 
     108    def test_get_codeblock_with_content_type(self): 
     109        self.assertEqual('text/x-python', 
     110            self.parser.get_codeblock(self.with_content_type, 1).content_type) 
     111        self.assertEqual('    line2', 
     112            self.parser.get_codeblock(self.with_content_type, 1).get_text()) 
     113        self.assertEqual('line2', 
     114            self.parser.get_codeblock(self.with_content_type, 1).get_code()) 
     115 
     116    def test_parse_text_code_text(self): 
     117        sections = self.parser.parse(self.text_code_text) 
     118        self.assertEqual(3, len(sections)) 
     119        self.assert_(not sections[0].is_codeblock()) 
     120        self.assertEqual('text\n\n', sections[0].get_text()) 
     121        self.assert_(sections[1].is_codeblock()) 
     122        self.assertEqual('    code\n\n', sections[1].get_text()) 
     123        self.assertEqual('code\n\n', sections[1].get_code()) 
     124        self.assert_(not sections[2].is_codeblock()) 
     125        self.assertEqual('text', sections[2].get_text()) 
     126 
     127    def test_parse_code_text_code(self): 
     128        sections = self.parser.parse(self.code_text_code) 
     129        self.assertEqual(3, len(sections)) 
     130        self.assert_(sections[0].is_codeblock()) 
     131        self.assertEqual('    code1\n\n', sections[0].get_text()) 
     132        self.assertEqual('code1\n\n', sections[0].get_code()) 
     133        self.assert_(not sections[1].is_codeblock()) 
     134        self.assertEqual('text\n\n', sections[1].get_text()) 
     135        self.assert_(sections[2].is_codeblock()) 
     136        self.assertEqual('    code2\n', sections[2].get_text()) 
     137        self.assertEqual('code2\n', sections[2].get_code()) 
  • bitstructures/trunk/substructure/views.py

    r25 r42  
    5353    if codeblock: 
    5454        if filename.lower().endswith('html'): 
    55             return HttpResponse(codeblock, mimetype='text/html') 
     55            return HttpResponse(codeblock.get_code(), mimetype='text/html') 
    5656        else: 
    57             return HttpResponse(codeblock, mimetype='text/plain') 
     57            return HttpResponse(codeblock.get_code(), mimetype='text/plain') 
    5858    else: 
    5959        raise Http404 
  • bitstructures/trunk/templates/substructure/entry.html

    r14 r42  
    1 {% load django.contrib.markup %} 
     1{% load substructure_tags %} 
    22 
    33<div class="entry"> 
     
    1313{% endif %} 
    1414 
    15 {{ entry.text|markdown }} 
     15{{ entry.text|syntax_highlighted_markdown }} 
    1616 
    1717</div>