1
# -*- coding: iso-8859-1 -*-
3
MoinMoin - Creole wiki markup parser
5
See http://wikicreole.org/ for latest specs.
8
* No markup allowed in headings.
9
Creole 1.0 does not require us to support this.
10
* No markup allowed in table headings.
11
Creole 1.0 does not require us to support this.
12
* No (non-bracketed) generic url recognition: this is "mission impossible"
13
except if you want to risk lots of false positives. Only known protocols
15
* We do not allow ":" before "//" italic markup to avoid urls with
16
unrecognized schemes (like wtf://server/path) triggering italic rendering
17
for the rest of the paragraph.
19
@copyright: 2007 MoinMoin:RadomirDopieralski (creole 0.5 implementation),
20
2007 MoinMoin:ThomasWaldmann (updates)
21
@license: GNU GPL, see COPYING for details.
26
from MoinMoin import config, wikiutil
27
from MoinMoin.macro import Macro
28
from MoinMoin.support.python_compatibility import rsplit # Needed for python 2.3
29
from _creole import Parser as CreoleParser
35
Glue the DocParser and DocEmitter with the
41
Dependencies = Dependencies
43
def __init__(self, raw, request, **kw):
44
"""Create a minimal Parser object with required attributes."""
46
self.request = request
47
self.form = request.form
50
def format(self, formatter):
51
"""Create and call the true parser and emitter."""
53
document = CreoleParser(self.raw).parse()
54
result = Emitter(document, formatter, self.request, Macro(self)).emit()
55
self.request.write(result)
58
# For the link targets:
59
proto = r'http|https|ftp|nntp|news|mailto|telnet|file|irc'
60
extern = r'(?P<extern_addr>(?P<extern_proto>%s):.*)' % proto
62
(?P<attach_scheme> attachment | drawing | image ):
66
(?P<inter_wiki> [A-Z][a-zA-Z]+ ) :
69
page = r'(?P<page_name> .* )'
74
Generate the output for the document
75
tree consisting of DocNodes.
78
addr_re = re.compile('|'.join([
83
]), re.X | re.U) # for addresses
85
def __init__(self, root, formatter, request, macro):
87
self.formatter = formatter
88
self.request = request
89
self.form = request.form
92
def get_text(self, node):
93
"""Try to emit whatever text is in the node."""
96
return node.children[0].content or ''
98
return node.content or ''
100
# *_emit methods for emitting nodes of the document:
102
def document_emit(self, node):
103
return self.emit_children(node)
105
def text_emit(self, node):
106
return self.formatter.text(node.content or '')
108
def separator_emit(self, node):
109
return self.formatter.rule()
111
def paragraph_emit(self, node):
113
self.formatter.paragraph(1),
114
self.emit_children(node),
115
self.formatter.paragraph(0),
118
def bullet_list_emit(self, node):
120
self.formatter.bullet_list(1),
121
self.emit_children(node),
122
self.formatter.bullet_list(0),
125
def number_list_emit(self, node):
127
self.formatter.number_list(1),
128
self.emit_children(node),
129
self.formatter.number_list(0),
132
def list_item_emit(self, node):
134
self.formatter.listitem(1),
135
self.emit_children(node),
136
self.formatter.listitem(0),
140
# def definition_list_emit(self, node):
142
# self.formatter.definition_list(1),
143
# self.emit_children(node),
144
# self.formatter.definition_list(0),
148
# def term_emit(self, node):
150
# self.formatter.definition_term(1),
151
# self.emit_children(node),
152
# self.formatter.definition_term(0),
156
# def definition_emit(self, node):
158
# self.formatter.definition_desc(1),
159
# self.emit_children(node),
160
# self.formatter.definition_desc(0),
163
def table_emit(self, node):
165
self.formatter.table(1, attrs=getattr(node, 'attrs', '')),
166
self.emit_children(node),
167
self.formatter.table(0),
170
def table_row_emit(self, node):
172
self.formatter.table_row(1, attrs=getattr(node, 'attrs', '')),
173
self.emit_children(node),
174
self.formatter.table_row(0),
177
def table_cell_emit(self, node):
179
self.formatter.table_cell(1, attrs=getattr(node, 'attrs', '')),
180
self.emit_children(node),
181
self.formatter.table_cell(0),
184
def table_head_emit(self, node):
186
self.formatter.rawHTML('<th>'),
187
self.emit_children(node),
188
self.formatter.rawHTML('</th>'),
191
def emphasis_emit(self, node):
193
self.formatter.emphasis(1),
194
self.emit_children(node),
195
self.formatter.emphasis(0),
199
# def quote_emit(self, node):
201
# self.formatter.rawHTML('<q>'),
202
# self.emit_children(node),
203
# self.formatter.rawHTML('</q>'),
206
def strong_emit(self, node):
208
self.formatter.strong(1),
209
self.emit_children(node),
210
self.formatter.strong(0),
214
# def smiley_emit(self, node):
215
# return self.formatter.smiley(node.content)
217
def header_emit(self, node):
219
pntt = '%s%s%d' % (self.formatter.page.page_name,
220
self.get_text(node), node.level)
221
ident = "head-%s" % sha.new(pntt.encode(config.charset)).hexdigest()
223
self.formatter.heading(1, node.level, id=ident),
224
self.formatter.text(node.content or ''),
225
self.formatter.heading(0, node.level),
228
def code_emit(self, node):
229
# XXX The current formatter will replace all spaces with , so we need
230
# to use rawHTML instead, until that is fixed.
232
# self.formatter.code(1),
233
# self.formatter.text(node.content or ''),
234
# self.formatter.code(0),
237
self.formatter.rawHTML('<tt>'),
238
self.formatter.text(node.content or ''),
239
self.formatter.rawHTML('</tt>'),
243
# def abbr_emit(self, node):
245
# self.formatter.rawHTML('<abbr title="%s">' % node.title),
246
# self.formatter.text(node.content or ''),
247
# self.formatter.rawHTML('</abbr>'),
250
def link_emit(self, node):
251
target = node.content
252
m = self.addr_re.match(target)
254
if m.group('page_name'):
256
word = m.group('page_name')
257
if word.startswith(wikiutil.PARENT_PREFIX):
258
word = word[wikiutil.PARENT_PREFIX_LEN:]
259
elif word.startswith(wikiutil.CHILD_PREFIX):
260
word = "%s/%s" % (self.formatter.page.page_name,
261
word[wikiutil.CHILD_PREFIX_LEN:])
263
parts = rsplit(word, "#", 1)
268
self.formatter.pagelink(1, word, anchor=anchor),
269
self.emit_children(node) or self.formatter.text(target),
270
self.formatter.pagelink(0, word),
272
elif m.group('extern_addr'):
274
address = m.group('extern_addr')
275
proto = m.group('extern_proto')
277
self.formatter.url(1, address, css=proto),
278
self.emit_children(node) or self.formatter.text(target),
279
self.formatter.url(0),
281
elif m.group('inter_wiki'):
283
wiki = m.group('inter_wiki')
284
page = m.group('inter_page')
286
self.formatter.interwikilink(1, wiki, page),
287
self.emit_children(node) or self.formatter.text(page),
288
self.formatter.interwikilink(0),
290
elif m.group('attach_scheme'):
291
# link to an attachment
292
scheme = m.group('attach_scheme')
293
attachment = m.group('attach_addr')
294
url = wikiutil.url_unquote(attachment, want_unicode=True)
295
text = self.get_text(node)
297
self.formatter.attachment_link(1, url),
298
self.formatter.text(text),
299
self.formatter.attachment_link(0)
301
return "".join(["[[", self.formatter.text(target), "]]"])
304
# def anchor_link_emit(self, node):
306
# self.formatter.url(1, node.content, css='anchor'),
307
# self.emit_children(node),
308
# self.formatter.url(0),
311
def image_emit(self, node):
312
target = node.content
313
text = self.get_text(node)
314
m = self.addr_re.match(target)
316
if m.group('page_name'):
318
url = wikiutil.url_unquote(target, want_unicode=True)
319
if target.startswith('#'):
320
return self.formatter.rawHTML(u'<a name="%s"></a>' % url[1:])
322
return self.formatter.attachment_image(
323
url, alt=text, html_class='image')
324
elif m.group('extern_addr'):
326
address = m.group('extern_addr')
327
proto = m.group('extern_proto')
328
url = wikiutil.url_unquote(address, want_unicode=True)
329
return self.formatter.image(
330
src=url, alt=text, html_class='external_image')
331
elif m.group('attach_scheme'):
332
# link to an attachment
333
scheme = m.group('attach_scheme')
334
attachment = m.group('attach_addr')
335
url = wikiutil.url_unquote(attachment, want_unicode=True)
336
if scheme == 'image':
337
return self.formatter.attachment_image(
338
url, alt=text, html_class='image')
339
elif scheme == 'drawing':
340
return self.formatter.attachment_drawing(url, text)
343
elif m.group('inter_wiki'):
346
# return "".join(["{{", self.formatter.text(target), "}}"])
347
url = wikiutil.url_unquote(node.content, want_unicode=True)
348
return self.formatter.attachment_inlined(url, text)
351
# def drawing_emit(self, node):
352
# url = wikiutil.url_unquote(node.content, want_unicode=True)
353
# text = self.get_text(node)
354
# return self.formatter.attachment_drawing(url, text)
357
# def figure_emit(self, node):
358
# text = self.get_text(node)
359
# url = wikiutil.url_unquote(node.content, want_unicode=True)
361
# self.formatter.rawHTML('<div class="figure">'),
362
# self.get_image(url, text), self.emit_children(node),
363
# self.formatter.rawHTML('</div>'),
367
# def bad_link_emit(self, node):
368
# return self.formatter.text(''.join([
370
# node.content or '',
374
def macro_emit(self, node):
375
macro_name = node.content
377
return self.formatter.macro(self.macro, macro_name, args)
380
# def section_emit(self, node):
382
# self.formatter.rawHTML(
383
# '<div class="%s" style="%s">' % (node.sect, node.style)),
384
# self.emit_children(node),
385
# self.formatter.rawHTML('</div>'),
388
def break_emit(self, node):
389
return self.formatter.linebreak(preformatted=0)
392
# def blockquote_emit(self, node):
394
# self.formatter.rawHTML('<blockquote>'),
395
# self.emit_children(node),
396
# self.formatter.rawHTML('</blockquote>'),
399
def preformatted_emit(self, node):
400
parser_name = getattr(node, 'sect', '')
402
# The formatter.parser will *sometimes* just return the result
403
# and *sometimes* try to write it directly. We need to take both
404
# cases into account!
405
lines = node.content.split(u'\n')
406
buf = StringIO.StringIO()
409
self.request.redirect(buf)
410
ret = self.formatter.parser(parser_name, lines)
412
self.request.redirect()
414
writ = buf.getvalue()
417
except wikiutil.PluginMissingError:
420
self.formatter.preformatted(1),
421
self.formatter.text(node.content),
422
self.formatter.preformatted(0),
425
def default_emit(self, node):
426
"""Fallback function for emitting unknown nodes."""
429
self.formatter.preformatted(1),
430
self.formatter.text('<%s>\n' % node.kind),
431
self.emit_children(node),
432
self.formatter.preformatted(0),
435
def emit_children(self, node):
436
"""Emit all the children of a node."""
438
return ''.join([self.emit_node(child) for child in node.children])
440
def emit_node(self, node):
441
"""Emit a single node."""
443
emit = getattr(self, '%s_emit' % node.kind, self.default_emit)
447
"""Emit the document represented by self.root DOM tree."""
449
# Try to disable 'smart' formatting if possible
450
magic_save = getattr(self.formatter, 'no_magic', False)
451
self.formatter.no_magic = True
453
self.emit_node(self.root),
455
# restore 'smart' formatting if it was set
456
self.formatter.no_magic = magic_save