Attachment 'text_plain.py.patch'
Download
Toggle line numbers
1 diff -r 63016f784d88 MoinMoin/formatter/text_plain.py
2 --- a/MoinMoin/formatter/text_plain.py Mon Apr 05 23:37:52 2010 +0200
3 +++ b/MoinMoin/formatter/text_plain.py Tue Apr 06 20:40:15 2010 +0000
4 @@ -3,11 +3,13 @@
5 MoinMoin - "text/plain" Formatter
6
7 @copyright: 2000-2002 Juergen Hermann <jh@web.de>
8 + 2007 by Timo Sirainen <tss@iki.fi>
9 @license: GNU GPL, see COPYING for details.
10 """
11
12 from MoinMoin.formatter import FormatterBase
13
14 +
15 class Formatter(FormatterBase):
16 """
17 Send plain text data.
18 @@ -20,168 +22,252 @@
19 self._in_code_area = 0
20 self._in_code_line = 0
21 self._code_area_state = [0, -1, -1, 0]
22 - self._in_list = 0
23 - self._did_para = 0
24 + self._lists = []
25 self._url = None
26 - self._text = None # XXX does not work with links in headings!!!!!
27 + self._text = None # XXX does not work with links in headings!!!!!
28 + self._text_stack = []
29 + self._skip_text = False
30 + self._wrap_skip_text = False
31 + self._textbuf = ''
32 + self._indent = 0
33 + self._listitem_on = []
34 + self._empty_line_count = 2
35 + self._paragraph_ended = False
36 + self._paragraph_skip_begin = True
37
38 def startDocument(self, pagename):
39 - line = u"*" * (len(pagename) + 2) + u'\n'
40 - return u"%s %s \n%s" % (line, pagename, line)
41 + line = u'\n'.rjust(len(pagename) + 2, u'*')
42 + return self.wrap(u"%s %s \n%s" % (line, pagename, line))
43
44 - def endDocument(self):
45 - return u'\n'
46 + def endContent(self):
47 + return self.flush(True)
48
49 def sysmsg(self, on, **kw):
50 - return (u'\n\n*** ', u' ***\n\n')[not on]
51 + return self.wrap((u'\n\n*** ', u' ***\n\n')[not on])
52
53 def pagelink(self, on, pagename='', page=None, **kw):
54 FormatterBase.pagelink(self, on, pagename, page, **kw)
55 - return (u">>", u"<<") [not on]
56 + if on:
57 + if not self._textbuf or self._textbuf[-1] in ('\n', ' '):
58 + result = self.wrap(u'<')
59 + else:
60 + result = self.wrap(u' <')
61 + self.text_on(True)
62 + self.add_missing_space()
63 + return result
64 + else:
65 + linktext = self._text
66 + self.text_off()
67 + orig_pagename = pagename
68 + if pagename.find('/'):
69 + pagename = pagename.replace('/', '.')
70 + pagename += '.txt'
71 + if linktext == orig_pagename:
72 + return self.wrap(u'%s>' % pagename)
73 + else:
74 + return self.wrap(u'%s> [%s]' % (linktext, pagename))
75
76 def interwikilink(self, on, interwiki='', pagename='', **kw):
77 if on:
78 + self.add_missing_space()
79 self._url = u"%s:%s" % (interwiki, pagename)
80 - self._text = []
81 + self.text_on()
82 return u''
83 else:
84 - if "".join(self._text) == self._url:
85 - self._url = None
86 - self._text = None
87 - return ''
88 + text = self._text
89 + self.text_off()
90 + if text == self._url:
91 + result = ''
92 else:
93 - self._url = None
94 - self._text = None
95 - return u' [%s]' % (self._url)
96 + result = self.wrap(u' [%s]' % (self._url))
97 + self._url = None
98 + return result
99
100 def url(self, on, url='', css=None, **kw):
101 if on:
102 + self.add_missing_space()
103 self._url = url
104 - self._text = []
105 + self.text_on()
106 return u''
107 else:
108 - if "".join(self._text) == self._url:
109 - self._url = None
110 - self._text = None
111 - return ''
112 + text = self._text
113 + self.text_off()
114 + if text == self._url or 'mailto:' + text == self._url:
115 + result = ''
116 else:
117 - self._url = None
118 - self._text = None
119 - return u' [%s]' % (self._url)
120 + result = self.wrap(u' [%s]' % (self._url))
121 + self._url = None
122 + return result
123
124 def attachment_link(self, on, url=None, **kw):
125 if on:
126 - return "["
127 - else:
128 - return "]"
129 + if 'title' in kw and kw['title']:
130 + if kw['title'] != url:
131 + return self.wrap(u'[attachment:%s ' % url)
132 + return self.wrap(u'[attachment:')
133 + return self.wrap(']')
134
135 def attachment_image(self, url, **kw):
136 title = ''
137 for a in (u'title', u'html__title', u'alt', u'html_alt'):
138 if a in kw:
139 title = ':' + kw[a]
140 - return "[image:%s%s]" % (url, title)
141 + return self.wrap("[image:%s%s]" % (url, title))
142
143 def attachment_drawing(self, url, text, **kw):
144 - return "[drawing:%s]" % text
145 + return self.wrap("[drawing:%s]" % text)
146
147 def text(self, text, **kw):
148 - self._did_para = 0
149 if self._text is not None:
150 - self._text.append(text)
151 - return text
152 + self._text += text
153 + if self._wrap_skip_text:
154 + return ''
155 + return self.wrap(text)
156
157 def rule(self, size=0, **kw):
158 size = min(size, 10)
159 ch = u"---~=*+#####"[size]
160 - return (ch * 79) + u'\n'
161 + self.paragraph_begin()
162 + result = self.wrap((ch * (79 - self._indent)))
163 + self.paragraph_end()
164 + return result
165
166 def strong(self, on, **kw):
167 - return u'*'
168 + if on:
169 + self.add_missing_space()
170 + return self.wrap(u'*')
171
172 def emphasis(self, on, **kw):
173 - return u'/'
174 + if on:
175 + self.add_missing_space()
176 + return self.wrap(u'/')
177
178 def highlight(self, on, **kw):
179 return u''
180
181 def number_list(self, on, type=None, start=None, **kw):
182 if on:
183 - self._in_list = 1
184 - return [u'\n', u'\n\n'][not self._did_para]
185 - else:
186 - self._in_list = 0
187 - if not self._did_para:
188 - self._did_para = 1
189 - return u'\n'
190 - return u''
191 + if self._lists:
192 + # No empty lines between sublists
193 + self._paragraph_ended = False
194 + self.paragraph_begin()
195 + self._lists.append(0)
196 + self._listitem_on.append(False)
197 + elif self._lists:
198 + self.paragraph_end()
199 + num = self._lists.pop()
200 + listitem_on = self._listitem_on.pop()
201 + if listitem_on:
202 + prefix = ' %d. ' % (num)
203 + self._indent -= len(prefix)
204 + return ''
205
206 def bullet_list(self, on, **kw):
207 if on:
208 - self._in_list = -1
209 - return [u'\n', u'\n\n'][not self._did_para]
210 + if self._lists:
211 + # No empty lines between sublists
212 + self._paragraph_ended = False
213 + self.paragraph_begin()
214 + self._lists.append(-1)
215 + self._listitem_on.append(False)
216 else:
217 - self._in_list = 0
218 - if not self._did_para:
219 - self._did_para = 1
220 - return u'\n'
221 - return u''
222 + self.paragraph_end()
223 + self._lists.pop()
224 + listitem_on = self._listitem_on.pop()
225 + if listitem_on:
226 + self._indent -= 3
227 + return ''
228
229 def listitem(self, on, **kw):
230 - if on:
231 - if self._in_list > 0:
232 - self._in_list += 1
233 - self._did_para = 1
234 - return ' %d. ' % (self._in_list-1, )
235 - elif self._in_list < 0:
236 - self._did_para = 1
237 - return u' * '
238 + self._paragraph_ended = False
239 + if not on:
240 + # we can't rely on this
241 + self.paragraph_end()
242 + return ''
243 +
244 + result = ''
245 + num = self._lists.pop()
246 + listitem_on = self._listitem_on.pop()
247 + if listitem_on and on:
248 + # we didn't receive on=False for previous listitem
249 + self.paragraph_end()
250 + if num >= 0:
251 + prefix = ' %d. ' % (num)
252 + self._indent -= len(prefix)
253 else:
254 - return u' * '
255 + self._indent -= 3
256 +
257 + if num >= 0:
258 + num += 1
259 + prefix = ' %d. ' % (num)
260 else:
261 - self._did_para = 1
262 - return u'\n'
263 + # FIXME: also before tables, at leat in LDA.Sieve.txt
264 + prefix = ' * '
265 + self._lists.append(num)
266 + self._listitem_on.append(on)
267 +
268 + result += self.wrap(prefix)
269 + self._indent += len(prefix)
270 + self._paragraph_skip_begin = True
271 + return result
272
273 def sup(self, on, **kw):
274 - return u'^'
275 + if on:
276 + return self.wrap(u'^')
277 + else:
278 + return ''
279
280 def sub(self, on, **kw):
281 - return u'_'
282 + return self.wrap(u'_')
283
284 def strike(self, on, **kw):
285 - return u'__'
286 + if on:
287 + self.add_missing_space()
288 + return self.wrap(u'__')
289
290 def code(self, on, **kw):
291 - #return [unichr(0x60), unichr(0xb4)][not on]
292 - return u"'" # avoid high-ascii
293 + if on:
294 + self.add_missing_space()
295 + return self.wrap(u"'")
296
297 def preformatted(self, on, **kw):
298 FormatterBase.preformatted(self, on)
299 - snip = u'---%<'
300 - snip = snip + (u'-' * (78 - len(snip)))
301 + snip = u'%s\n' % u'---%<'.ljust(78 - self._indent, u'-')
302 if on:
303 - return u'\n' + snip + u'\n'
304 + self.paragraph_begin()
305 + return self.wrap(snip)
306 else:
307 - return snip + u'\n'
308 + if self._textbuf and not self._textbuf.endswith('\n'):
309 + self._textbuf += '\n'
310 + result = self.wrap(snip)
311 + self.paragraph_end()
312 + return result
313
314 def small(self, on, **kw):
315 + if on:
316 + self.add_missing_space()
317 return u''
318
319 def big(self, on, **kw):
320 + if on:
321 + self.add_missing_space()
322 return u''
323
324 - def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1, msg=None):
325 - snip = u'---CodeArea'
326 - snip = snip + (u'-' * (78 - len(snip)))
327 + def code_area(self, on, code_id, code_type='code', show=0, start=-1,
328 + step=-1, msg=None):
329 + snip = u'%s\n' % u'---CodeArea'.ljust(78 - self._indent, u'-')
330 if on:
331 + self.paragraph_begin()
332 self._in_code_area = 1
333 self._in_code_line = 0
334 self._code_area_state = [show, start, step, start]
335 - return u'\n' + snip + u'\n'
336 + return self.wrap(snip)
337 else:
338 if self._in_code_line:
339 - return self.code_line(0) + snip + u'\n'
340 - return snip + u'\n'
341 + return self.wrap(self.code_line(0) + snip)
342 + result = self.wrap(snip)
343 + self.paragraph_end()
344 + return result
345
346 def code_line(self, on):
347 res = u''
348 @@ -192,70 +278,302 @@
349 res += u' %4d ' % self._code_area_state[3]
350 self._code_area_state[3] += self._code_area_state[2]
351 self._in_code_line = on != 0
352 - return res
353 + return self.wrap(res)
354
355 def code_token(self, on, tok_type):
356 return ""
357
358 + def add_missing_space(self):
359 + if self._textbuf and self._textbuf[-1].isalnum():
360 + self._textbuf += ' '
361 +
362 def paragraph(self, on, **kw):
363 FormatterBase.paragraph(self, on)
364 - if self._did_para:
365 - on = 0
366 - return [u'\n', u''][not on]
367 + if on:
368 + self.paragraph_begin()
369 + else:
370 + self.paragraph_end()
371 + return ''
372
373 def linebreak(self, preformatted=1):
374 - return u'\n'
375 + return self.wrap(u'\n')
376
377 def smiley(self, text):
378 - return text
379 + return self.wrap(text)
380
381 def heading(self, on, depth, **kw):
382 if on:
383 - self._text = []
384 - return '\n\n'
385 + self.paragraph_begin()
386 + self.text_on()
387 + result = ''
388 else:
389 - result = u'\n%s\n\n' % (u'=' * len("".join(self._text)))
390 - self._text = None
391 - return result
392 + if depth == 1:
393 + ch = u'='
394 + else:
395 + ch = u'-'
396 +
397 + result = u'\n%s\n' % (ch * len(self._text))
398 + self.text_off()
399 + result = self.wrap(result)
400 + self.paragraph_end()
401 + return result
402 +
403 + def get_table_sep(self, col_widths):
404 + result = ''
405 + for width in col_widths:
406 + result += '+' + ('-' * width)
407 + return result + '+\n'
408 +
409 + def fix_col_widths(self):
410 + min_widths = self._table_column_min_len
411 + max_widths = self._table_column_max_len
412 + max_len = sum(max_widths)
413 + # take the needed space equally from all columns
414 + count = len(max_widths)
415 + idx, skip = 0, 0
416 + available_len = 79 - count - 1
417 + while max_len > available_len:
418 + if max_widths[idx] > min_widths[idx]:
419 + max_widths[idx] -= 1
420 + max_len -= 1
421 + skip = 0
422 + else:
423 + skip += 1
424 + if skip == count:
425 + # there are only too wide columns
426 + break
427 + if idx == count - 1:
428 + idx = 0
429 + else:
430 + idx += 1
431 + return max_widths
432
433 def table(self, on, attrs={}, **kw):
434 - return u''
435 + if on:
436 + self._table = []
437 + self._table_column_min_len = []
438 + self._table_column_max_len = []
439 + result = self.flush(True)
440 + else:
441 + result = u''
442 + col_widths = self.fix_col_widths()
443 + for row in self._table:
444 + result += self.get_table_sep(col_widths)
445 + more = True
446 + while more:
447 + more = False
448 + num = 0
449 + result += '|'
450 + for col in row:
451 + # break at next LF
452 + lf_idx = col.find('\n')
453 + if lf_idx != -1:
454 + more = True
455 + col_len = lf_idx
456 + next_idx = lf_idx + 1
457 + else:
458 + col_len = len(col)
459 + next_idx = col_len
460 + # possibly break earlier if we need to wrap
461 + if col_len > col_widths[num]:
462 + idx = col.rfind(' ', 0, col_widths[num])
463 + if idx == -1:
464 + idx = col.find(' ', col_widths[num])
465 + if idx != -1:
466 + col_len = idx
467 + next_idx = idx + 1
468 + more = True
469 + result += ' ' + col[:col_len]
470 + result += (' ' * (col_widths[num] - col_len - 1)) + '|'
471 + row[num] = col[next_idx:]
472 + num += 1
473 + result += '\n'
474 + result += self.get_table_sep(col_widths)
475 + self._table = None
476 + self._table_column_min_len = None
477 + self._table_column_max_len = None
478 + self._empty_line_count = 0
479 + self.paragraph_end()
480 + return result
481
482 def table_row(self, on, attrs={}, **kw):
483 + if on:
484 + self._table.append([])
485 return u''
486
487 def table_cell(self, on, attrs={}, **kw):
488 + if on:
489 + self.text_on()
490 + self._wrap_skip_text = True
491 + else:
492 + # keep track of the longest word and the longest line in the cell
493 + self._text = self._text.strip()
494 + max_line_len = 0
495 + max_word_len = 0
496 + for line in self._text.split('\n'):
497 + if len(line) > max_line_len:
498 + max_line_len = len(line)
499 + for word in self._text.split(' '):
500 + if len(word) > max_word_len:
501 + max_word_len = len(word)
502 + # one preceding and trailing cell whitespace
503 + max_word_len += 2
504 + max_line_len += 2
505 +
506 + rownum = len(self._table) - 1
507 + colnum = len(self._table[rownum])
508 + if len(self._table_column_max_len) <= colnum:
509 + self._table_column_min_len.append(max_word_len)
510 + self._table_column_max_len.append(max_line_len)
511 + else:
512 + if max_word_len > self._table_column_min_len[colnum]:
513 + self._table_column_min_len[colnum] = max_word_len
514 + if self._table_column_max_len[colnum] < max_line_len:
515 + self._table_column_max_len[colnum] = max_line_len
516 + self._table[rownum].append(self._text)
517 + self.text_off()
518 return u''
519
520 def underline(self, on, **kw):
521 - return u'_'
522 + return self.wrap(u'_')
523
524 def definition_list(self, on, **kw):
525 + if on:
526 + self.paragraph_begin()
527 + else:
528 + self.paragraph_end()
529 return u''
530
531 def definition_term(self, on, compact=0, **kw):
532 result = u''
533 - if not compact:
534 - result = result + u'\n'
535 + #if not compact:
536 + # result = result + u'\n'
537 if not on:
538 - result = result + u':\n'
539 - return result
540 + result = result + u':'
541 + return self.wrap(result)
542
543 def definition_desc(self, on, **kw):
544 - return [u' ', u'\n'][not on]
545 + if on:
546 + self._indent += 2
547 + self.paragraph_begin()
548 + else:
549 + self.paragraph_end()
550 + self._textbuf += '\n'
551 + self._indent -= 2
552 + return ''
553
554 def image(self, src=None, **kw):
555 for a in (u'title', u'html__title', u'alt', u'html_alt'):
556 if a in kw:
557 - return kw[a]
558 - return u''
559 -
560 - def transclusion(self, on, **kw):
561 - return u''
562 -
563 - def transclusion_param(self, **kw):
564 - return u''
565 + return self.wrap(kw[a] + ' [' + src + ']')
566 + return self.wrap('[' + src + ']')
567
568 def lang(self, on, lang_name):
569 return ''
570
571 + def paragraph_begin(self):
572 + if self._paragraph_ended:
573 + self._textbuf += '\n'
574 + elif not self._paragraph_skip_begin:
575 + if self._textbuf and not self._textbuf.endswith('\n'):
576 + self._textbuf += '\n'
577 + self._paragraph_ended = False
578 + self._paragraph_skip_begin = False
579 +
580 + def paragraph_end(self):
581 + if self._textbuf and not self._textbuf.endswith('\n'):
582 + self._textbuf += '\n'
583 + self._paragraph_ended = True
584 +
585 + def wrap(self, text):
586 + if not text:
587 + return ''
588 + if self._wrap_skip_text:
589 + # we're inside table
590 + #self._text += 'w{' + text + '}'
591 + self._text += text
592 + return ''
593 +
594 + self._paragraph_ended = False
595 + self._paragraph_skip_begin = False
596 +
597 + # add indents after all LFs. kind of dirty to split twice though..
598 + lines = text.split('\n')
599 + text = lines.pop(0)
600 + while lines:
601 + text += '\n%s%s' % (' ' * self._indent, lines.pop(0))
602 +
603 + if not self._textbuf or self._textbuf.endswith('\n'):
604 + self._textbuf += ' ' * self._indent
605 + self._textbuf += text
606 +
607 + lines = self._textbuf.split('\n')
608 + self._textbuf = ''
609 + text = ''
610 + while lines:
611 + self._textbuf += lines.pop(0)
612 + if lines:
613 + # LFs found
614 + text += self.flush(True)
615 + if len(self._textbuf) > 80 and \
616 + self._textbuf.find(' ', self._indent) != -1:
617 + # wrap time
618 + text += self.flush(False)
619 + return text
620 +
621 + def flush(self, addlf):
622 + result = ''
623 +
624 + while len(self._textbuf) >= 80:
625 + # need to wrap
626 + last_space = self._textbuf.rfind(' ', self._indent, 80)
627 + if last_space == -1:
628 + # a long line. split at the next possible space
629 + last_space = self._textbuf.find(' ', self._indent)
630 + if last_space == -1:
631 + break
632 + result += self._textbuf[:last_space] + '\n'
633 + self._empty_line_count = 0
634 + self._textbuf = ' ' * self._indent + self._textbuf[last_space + 1:]
635 + self._textbuf = self._textbuf.rstrip()
636 +
637 + if not self._textbuf:
638 + if not addlf:
639 + return result
640 + self._empty_line_count += 1
641 + if self._empty_line_count >= 2:
642 + return result
643 + else:
644 + self._empty_line_count = 0
645 +
646 + if addlf:
647 + result += self._textbuf + '\n'
648 + self._textbuf = ''
649 + return result
650 +
651 + def text_on(self, skip_text=False):
652 + if self._text is None:
653 + self._text_stack.append(None)
654 + else:
655 + self._text_stack.append(self._text)
656 + #self._text_stack.append('[' + self._text + ']')
657 + self._text_stack.append(self._skip_text)
658 + self._text_stack.append(self._wrap_skip_text)
659 + self._text = ""
660 + self._skip_text = skip_text
661 + if skip_text:
662 + self._wrap_skip_text = True
663 +
664 + def text_off(self):
665 + prev_skip_text = self._skip_text
666 + self._wrap_skip_text = self._text_stack.pop()
667 + self._skip_text = self._text_stack.pop()
668 + old_text = self._text_stack.pop()
669 + if old_text is None:
670 + self._text = None
671 + else:
672 + if not prev_skip_text:
673 + #self._text = 'o#' + old_text + '#|#' + self._text + '#'
674 + self._text = old_text + self._text
675 + else:
676 + self._text = old_text
677
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.