1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
import re
import os
import pygments
from pygments import formatters, util, lexers
import blogofile_bf as bf
config = {"name": "Syntax Highlighter",
"description": "Highlights blocks of code based on syntax",
"author": "Ryan McGuire",
"css_dir": "/css",
"preload_styles": []}
def init():
#This filter normally only loads pygments styles when needed.
#This will force a particular style to get loaded at startup.
for style in bf.config.filters.syntax_highlight.preload_styles:
css_class = "pygments_{0}".format(style)
formatter = pygments.formatters.HtmlFormatter(
linenos=False, cssclass=css_class, style=style)
write_pygments_css(style, formatter)
example = """
This is normal text.
The following is a python code block:
$$code(lang=python)
import this
prices = {'apple' : 0.50, #Prices of fruit
'orange' : 0.65,
'pear' : 0.90}
def print_prices():
for fruit, price in prices.items():
print "An %s costs %s" % (fruit, price)
$$/code
This is a ruby code block:
$$code(lang=ruby)
class Person
attr_reader :name, :age
def initialize(name, age)
@name, @age = name, age
end
def <=>(person) # Comparison operator for sorting
@age <=> person.age
end
def to_s
"#@name (#@age)"
end
end
group = [
Person.new("Bob", 33),
Person.new("Chris", 16),
Person.new("Ash", 23)
]
puts group.sort.reverse
$$/code
This is normal text
"""
css_files_written = set()
code_block_re = re.compile(
r"(?:^|\s)" # $$code Must start as a new word
r"\$\$code" # $$code is the start of the block
r"(?P<args>\([^\r\n]*\))?" # optional arguments are passed in brackets
r"[^\r\n]*\r?\n" # ignore everything else on the 1st line
r"(?P<code>.*?)\s\$\$/code" # code block continues until $$/code
, re.DOTALL)
argument_re = re.compile(
r"[ ]*" # eat spaces at the beginning
"(?P<arg>" # start of argument
".*?" # the name of the argument
"=" # the assignment
r"""(?:(?:[^"']*?)""" # a non-quoted value
r"""|(?:"[^"]*")""" # or, a double-quoted value
r"""|(?:'[^']*')))""" # or, a single-quoted value
"[ ]*" # eat spaces at the end
"[,\r\n]" # ends in a comma or newline
)
def highlight_code(code, language, formatter):
try:
lexer = pygments.lexers.get_lexer_by_name(language)
except pygments.util.ClassNotFound:
lexer = pygments.lexers.get_lexer_by_name("text")
#Highlight with pygments and surround by blank lines
#(blank lines required for markdown syntax)
highlighted = "\n\n{0}\n\n".format(
pygments.highlight(code, lexer, formatter))
return highlighted
def parse_args(args):
#Make sure the args are newline terminated (req'd by regex)
opts = {}
if args is None:
return opts
args = args.lstrip("(").rstrip(")")
if args[-1] != "\n":
args = args+"\n"
for m in argument_re.finditer(args):
arg = m.group('arg').split('=')
opts[arg[0]] = arg[1]
return opts
def write_pygments_css(style, formatter,
location=bf.config.filters.syntax_highlight.css_dir):
path = bf.util.path_join("_site", bf.util.fs_site_path_helper(location))
bf.util.mkdir(path)
css_file = "pygments_{0}.css".format(style)
css_path = os.path.join(path, css_file)
css_site_path = css_path.replace("_site", "")
if css_site_path in css_files_written:
return #already written, no need to overwrite it.
f = open(css_path, "w")
css_class = ".pygments_{0}".format(style)
f.write(formatter.get_style_defs(css_class))
f.close()
css_files_written.add(css_site_path)
def run(src):
substitutions = {}
for m in code_block_re.finditer(src):
args = parse_args(m.group('args'))
#Make default args
if args.has_key('lang'):
lang = args['lang']
elif args.has_key('language'):
lang = args['language']
else:
lang = 'text'
try:
if args.has_key('linenums'):
linenums = args['linenums']
elif args.has_key("linenos"):
linenums = args['linenos']
if linenums.lower().strip() == "true":
linenums = True
else:
linenums = False
except:
linenums = False
try:
style = args['style']
except KeyError:
style = bf.config.filters.syntax_highlight.style
try:
css_class = args['cssclass']
except KeyError:
css_class = "pygments_{0}".format(style)
formatter = pygments.formatters.HtmlFormatter(
linenos=linenums, cssclass=css_class, style=style)
write_pygments_css(style, formatter)
substitutions[m.group()] = highlight_code(
m.group('code'), lang, formatter)
if len(substitutions) > 0:
p = re.compile('|'.join(map(re.escape, substitutions)))
src = p.sub(lambda x: substitutions[x.group(0)], src)
return src
else:
return src
|