view MoinMoin/formatter/text_plain.py @ 6111:1fdd537e9d83

SubProcess: reimplement exec_cmd subclassing Popen and overriding some methods isn't pretty. the code we have was written for py 2.4 or so and the py 2.7 Popen looked quite different. this way with the timer should be less problematic.
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Tue, 06 Sep 2016 04:39:28 +0200
parents 666e359493f5
children
line wrap: on
line source
# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - "text/plain" Formatter

    @copyright: 2000-2002 Juergen Hermann <jh@web.de>
                2007 by Timo Sirainen <tss@iki.fi>
    @license: GNU GPL, see COPYING for details.
"""

from MoinMoin.formatter import FormatterBase


class Formatter(FormatterBase):
    """
        Send plain text data.
    """

    hardspace = u' '

    def __init__(self, request, **kw):
        FormatterBase.__init__(self, request, **kw)
        self._in_code_area = 0
        self._in_code_line = 0
        self._code_area_state = [0, -1, -1, 0]
        self._lists = []
        self._url = None
        self._text = None  # XXX does not work with links in headings!!!!!
        self._text_stack = []
        self._skip_text = False
        self._wrap_skip_text = False
        self._textbuf = ''
        self._indent = 0
        self._listitem_on = []
        self._empty_line_count = 2
        self._paragraph_ended = False
        self._paragraph_skip_begin = True

    def startDocument(self, pagename):
        line = u'\n'.rjust(len(pagename) + 2, u'*')
        return self.wrap(u"%s %s \n%s" % (line, pagename, line))

    def endContent(self):
        return self.flush(True)

    def sysmsg(self, on, **kw):
        return self.wrap((u'\n\n*** ', u' ***\n\n')[not on])

    def pagelink(self, on, pagename='', page=None, **kw):
        FormatterBase.pagelink(self, on, pagename, page, **kw)
        if on:
            if not self._textbuf or self._textbuf[-1] in ('\n', ' '):
                result = self.wrap(u'<')
            else:
                result = self.wrap(u' <')
            self.text_on(True)
            self.add_missing_space()
            return result
        else:
            linktext = self._text
            self.text_off()
            orig_pagename = pagename
            if pagename.find('/'):
                pagename = pagename.replace('/', '.')
            pagename += '.txt'
            if linktext == orig_pagename:
                return self.wrap(u'%s>' % pagename)
            else:
                return self.wrap(u'%s> [%s]' % (linktext, pagename))

    def interwikilink(self, on, interwiki='', pagename='', **kw):
        if on:
            self.add_missing_space()
            self._url = u"%s:%s" % (interwiki, pagename)
            self.text_on()
            return u''
        else:
            text = self._text
            self.text_off()
            if text == self._url:
                result = ''
            else:
                result = self.wrap(u' [%s]' % (self._url))
            self._url = None
            return result

    def url(self, on, url='', css=None, **kw):
        if on:
            self.add_missing_space()
            self._url = url
            self.text_on()
            return u''
        else:
            text = self._text
            self.text_off()
            if text == self._url or 'mailto:' + text == self._url:
                result = ''
            else:
                result = self.wrap(u' [%s]' % (self._url))
            self._url = None
            return result

    def attachment_link(self, on, url=None, **kw):
        if on:
            if 'title' in kw and kw['title']:
                if kw['title'] != url:
                    return self.wrap(u'[attachment:%s ' % url)
            return self.wrap(u'[attachment:')
        return self.wrap(']')

    def attachment_image(self, url, **kw):
        title = ''
        for a in (u'title', u'html__title', u'alt', u'html_alt'):
            if a in kw:
                title = ':' + kw[a]
        return self.wrap("[image:%s%s]" % (url, title))

    def attachment_drawing(self, url, text, **kw):
        return self.wrap("[drawing:%s]" % text)

    def text(self, text, **kw):
        if self._text is not None:
            self._text += text
        if self._wrap_skip_text:
            return ''
        return self.wrap(text)

    def rule(self, size=0, **kw):
        size = min(size, 10)
        ch = u"---~=*+#####"[size]
        self.paragraph_begin()
        result = self.wrap((ch * (79 - self._indent)))
        self.paragraph_end()
        return result

    def strong(self, on, **kw):
        if on:
            self.add_missing_space()
        return self.wrap(u'*')

    def emphasis(self, on, **kw):
        if on:
            self.add_missing_space()
        return self.wrap(u'/')

    def highlight(self, on, **kw):
        return u''

    def number_list(self, on, type=None, start=None, **kw):
        if on:
            if self._lists:
                # No empty lines between sublists
                self._paragraph_ended = False
            self.paragraph_begin()
            self._lists.append(0)
            self._listitem_on.append(False)
        elif self._lists:
            self.paragraph_end()
            num = self._lists.pop()
            listitem_on = self._listitem_on.pop()
            if listitem_on:
                prefix = ' %d. ' % (num)
                self._indent -= len(prefix)
        return ''

    def bullet_list(self, on, **kw):
        if on:
            if self._lists:
                # No empty lines between sublists
                self._paragraph_ended = False
            self.paragraph_begin()
            self._lists.append(-1)
            self._listitem_on.append(False)
        elif self._lists:
            self.paragraph_end()
            self._lists.pop()
            listitem_on = self._listitem_on.pop()
            if listitem_on:
                self._indent -= 3
        return ''

    def listitem(self, on, **kw):
        self._paragraph_ended = False
        if not on:
            # we can't rely on this
            self.paragraph_end()
            return ''

        result = ''
        num = self._lists.pop()
        listitem_on = self._listitem_on.pop()
        if listitem_on and on:
            # we didn't receive on=False for previous listitem
            self.paragraph_end()
            if num >= 0:
                prefix = ' %d. ' % (num)
                self._indent -= len(prefix)
            else:
                self._indent -= 3

        if num >= 0:
            num += 1
            prefix = ' %d. ' % (num)
        else:
            # FIXME: also before tables, at leat in LDA.Sieve.txt
            prefix = ' * '
        self._lists.append(num)
        self._listitem_on.append(on)

        result += self.wrap(prefix)
        self._indent += len(prefix)
        self._paragraph_skip_begin = True
        return result

    def sup(self, on, **kw):
        if on:
            return self.wrap(u'^')
        else:
            return ''

    def sub(self, on, **kw):
        return self.wrap(u'_')

    def strike(self, on, **kw):
        if on:
            self.add_missing_space()
        return self.wrap(u'__')

    def code(self, on, **kw):
        if on:
            self.add_missing_space()
        return self.wrap(u"'")

    def preformatted(self, on, **kw):
        FormatterBase.preformatted(self, on)
        snip = u'%s\n' % u'---%<'.ljust(78 - self._indent, u'-')
        if on:
            self.paragraph_begin()
            return self.wrap(snip)
        else:
            if self._textbuf and not self._textbuf.endswith('\n'):
                self._textbuf += '\n'
            result = self.wrap(snip)
            self.paragraph_end()
            return result

    def small(self, on, **kw):
        if on:
            self.add_missing_space()
        return u''

    def big(self, on, **kw):
        if on:
            self.add_missing_space()
        return u''

    def code_area(self, on, code_id, code_type='code', show=0, start=-1,
                  step=-1, msg=None):
        snip = u'%s\n' % u'---CodeArea'.ljust(78 - self._indent, u'-')
        if on:
            self.paragraph_begin()
            self._in_code_area = 1
            self._in_code_line = 0
            self._code_area_state = [show, start, step, start]
            return self.wrap(snip)
        else:
            if self._in_code_line:
                return self.wrap(self.code_line(0) + snip)
            result = self.wrap(snip)
            self.paragraph_end()
            return result

    def code_line(self, on):
        res = u''
        if not on or (on and self._in_code_line):
            res += u'\n'
        if on:
            if self._code_area_state[0] > 0:
                res += u' %4d  ' % self._code_area_state[3]
                self._code_area_state[3] += self._code_area_state[2]
        self._in_code_line = on != 0
        return self.wrap(res)

    def code_token(self, on, tok_type):
        return ""

    def add_missing_space(self):
        if self._textbuf and self._textbuf[-1].isalnum():
            self._textbuf += ' '

    def paragraph(self, on, **kw):
        FormatterBase.paragraph(self, on)
        if on:
            self.paragraph_begin()
        else:
            self.paragraph_end()
        return ''

    def linebreak(self, preformatted=1):
        return self.wrap(u'\n')

    def smiley(self, text):
        return self.wrap(text)

    def heading(self, on, depth, **kw):
        if on:
            self.paragraph_begin()
            self.text_on()
            result = ''
        else:
            if depth == 1:
                ch = u'='
            else:
                ch = u'-'

            result = u'\n%s\n' % (ch * len(self._text))
            self.text_off()
            result = self.wrap(result)
            self.paragraph_end()
        return result

    def get_table_sep(self, col_widths):
        result = ''
        for width in col_widths:
            result += '+' + ('-' * width)
        return result + '+\n'

    def fix_col_widths(self):
        min_widths = self._table_column_min_len
        max_widths = self._table_column_max_len
        max_len = sum(max_widths)
        # take the needed space equally from all columns
        count = len(max_widths)
        idx, skip = 0, 0
        available_len = 79 - count - 1
        while max_len > available_len:
            if max_widths[idx] > min_widths[idx]:
                max_widths[idx] -= 1
                max_len -= 1
                skip = 0
            else:
                skip += 1
                if skip == count:
                    # there are only too wide columns
                    break
            if idx == count - 1:
                idx = 0
            else:
                idx += 1
        return max_widths

    def table(self, on, attrs={}, **kw):
        if on:
            self._table = []
            self._table_column_min_len = []
            self._table_column_max_len = []
            result = self.flush(True)
        else:
            result = u''
            col_widths = self.fix_col_widths()
            for row in self._table:
                result += self.get_table_sep(col_widths)
                more = True
                while more:
                    more = False
                    num = 0
                    result += '|'
                    for col in row:
                        # break at next LF
                        lf_idx = col.find('\n')
                        if lf_idx != -1:
                            more = True
                            col_len = lf_idx
                            next_idx = lf_idx + 1
                        else:
                            col_len = len(col)
                            next_idx = col_len
                        # possibly break earlier if we need to wrap
                        if col_len > col_widths[num]:
                            idx = col.rfind(' ', 0, col_widths[num])
                            if idx == -1:
                                idx = col.find(' ', col_widths[num])
                            if idx != -1:
                                col_len = idx
                                next_idx = idx + 1
                            more = True
                        result += ' ' + col[:col_len]
                        result += (' ' * (col_widths[num] - col_len - 1)) + '|'
                        row[num] = col[next_idx:]
                        num += 1
                    result += '\n'
            result += self.get_table_sep(col_widths)
            self._table = None
            self._table_column_min_len = None
            self._table_column_max_len = None
            self._empty_line_count = 0
            self.paragraph_end()
        return result

    def table_row(self, on, attrs={}, **kw):
        if on:
            self._table.append([])
        return u''

    def table_cell(self, on, attrs={}, **kw):
        if on:
            self.text_on()
            self._wrap_skip_text = True
        else:
            # keep track of the longest word and the longest line in the cell
            self._text = self._text.strip()
            max_line_len = 0
            max_word_len = 0
            for line in self._text.split('\n'):
                if len(line) > max_line_len:
                    max_line_len = len(line)
            for word in self._text.split(' '):
                if len(word) > max_word_len:
                    max_word_len = len(word)
            # one preceding and trailing cell whitespace
            max_word_len += 2
            max_line_len += 2

            rownum = len(self._table) - 1
            colnum = len(self._table[rownum])
            if len(self._table_column_max_len) <= colnum:
                self._table_column_min_len.append(max_word_len)
                self._table_column_max_len.append(max_line_len)
            else:
                if max_word_len > self._table_column_min_len[colnum]:
                    self._table_column_min_len[colnum] = max_word_len
                if self._table_column_max_len[colnum] < max_line_len:
                    self._table_column_max_len[colnum] = max_line_len
            self._table[rownum].append(self._text)
            self.text_off()
        return u''

    def underline(self, on, **kw):
        return self.wrap(u'_')

    def definition_list(self, on, **kw):
        if on:
            self.paragraph_begin()
        else:
            self.paragraph_end()
        return u''

    def definition_term(self, on, compact=0, **kw):
        result = u''
        #if not compact:
        #    result = result + u'\n'
        if not on:
            result = result + u':'
        return self.wrap(result)

    def definition_desc(self, on, **kw):
        if on:
            self._indent += 2
            self.paragraph_begin()
        else:
            self.paragraph_end()
            self._textbuf += '\n'
            self._indent -= 2
        return ''

    def image(self, src=None, **kw):
        for a in (u'title', u'html__title', u'alt', u'html_alt'):
            if a in kw:
                return self.wrap(kw[a] + ' [' + src + ']')
        return self.wrap('[' + src + ']')

    def lang(self, on, lang_name):
        return ''

    def paragraph_begin(self):
        if self._paragraph_ended:
            self._textbuf += '\n'
        elif not self._paragraph_skip_begin:
            if self._textbuf and not self._textbuf.endswith('\n'):
                self._textbuf += '\n'
        self._paragraph_ended = False
        self._paragraph_skip_begin = False

    def paragraph_end(self):
        if self._textbuf and not self._textbuf.endswith('\n'):
            self._textbuf += '\n'
        self._paragraph_ended = True

    def wrap(self, text):
        if not text:
            return ''
        if self._wrap_skip_text:
            # we're inside table
            #self._text += 'w{' + text + '}'
            self._text += text
            return ''

        self._paragraph_ended = False
        self._paragraph_skip_begin = False

        # add indents after all LFs. kind of dirty to split twice though..
        lines = text.split('\n')
        text = lines.pop(0)
        while lines:
            text += '\n%s%s' % (' ' * self._indent, lines.pop(0))

        if not self._textbuf or self._textbuf.endswith('\n'):
            self._textbuf += ' ' * self._indent
        self._textbuf += text

        lines = self._textbuf.split('\n')
        self._textbuf = ''
        text = ''
        while lines:
            self._textbuf += lines.pop(0)
            if lines:
                # LFs found
                text += self.flush(True)
            if len(self._textbuf) > 80 and \
                    self._textbuf.find(' ', self._indent) != -1:
                # wrap time
                text += self.flush(False)
        return text

    def flush(self, addlf):
        result = ''

        while len(self._textbuf) >= 80:
            # need to wrap
            last_space = self._textbuf.rfind(' ', self._indent, 80)
            if last_space == -1:
                # a long line. split at the next possible space
                last_space = self._textbuf.find(' ', self._indent)
                if last_space == -1:
                    break
            result += self._textbuf[:last_space] + '\n'
            self._empty_line_count = 0
            self._textbuf = ' ' * self._indent + self._textbuf[last_space + 1:]
        self._textbuf = self._textbuf.rstrip()

        if not self._textbuf:
            if not addlf:
                return result
            self._empty_line_count += 1
            if self._empty_line_count >= 2:
                return result
        else:
            self._empty_line_count = 0

        if addlf:
            result += self._textbuf + '\n'
            self._textbuf = ''
        return result

    def text_on(self, skip_text=False):
        if self._text is None:
            self._text_stack.append(None)
        else:
            self._text_stack.append(self._text)
            #self._text_stack.append('[' + self._text + ']')
        self._text_stack.append(self._skip_text)
        self._text_stack.append(self._wrap_skip_text)
        self._text = ""
        self._skip_text = skip_text
        if skip_text:
            self._wrap_skip_text = True

    def text_off(self):
        prev_skip_text = self._skip_text
        self._wrap_skip_text = self._text_stack.pop()
        self._skip_text = self._text_stack.pop()
        old_text = self._text_stack.pop()
        if old_text is None:
            self._text = None
        else:
            if not prev_skip_text:
                #self._text = 'o#' + old_text + '#|#' + self._text + '#'
                self._text = old_text + self._text
            else:
                self._text = old_text