MoinMoin/script/migration/text_moin158_wiki.py
author Thomas Waldmann <tw AT waldmann-edv DOT de>
Wed, 11 Feb 2009 02:34:33 +0100
changeset 4569 3caaa8c74c41
parent 4363 817d99d715fe
permissions -rw-r--r--
wikiutil: replace moin's cgi/urllib wrappers by calls to werkzeug.utils code
tw@2731
     1
# -*- coding: iso-8859-1 -*-
tw@2731
     2
"""
tw@2731
     3
    MoinMoin - MoinMoin Wiki Markup Parser
tw@2731
     4
tw@2731
     5
    @copyright: 2000, 2001, 2002 by Jürgen Hermann <jh@web.de>
tw@2731
     6
    @license: GNU GPL, see COPYING for details.
tw@2731
     7
"""
tw@2731
     8
tw@2731
     9
import os, re
tw@2731
    10
from MoinMoin import config, wikiutil
tw@2731
    11
from MoinMoin import macro as wikimacro
tw@2731
    12
from MoinMoin.Page import Page
tw@2731
    13
from MoinMoin.util import web
tw@2731
    14
tw@2731
    15
Dependencies = []
tw@2731
    16
tw@2731
    17
class Parser:
tw@2731
    18
    """
tw@2731
    19
        Object that turns Wiki markup into HTML.
tw@2731
    20
tw@2731
    21
        All formatting commands can be parsed one line at a time, though
tw@2731
    22
        some state is carried over between lines.
tw@2731
    23
tw@2731
    24
        Methods named like _*_repl() are responsible to handle the named regex
tw@2731
    25
        patterns defined in print_html().
tw@2731
    26
    """
tw@2731
    27
tw@2731
    28
    # allow caching
tw@2731
    29
    caching = 1
tw@2731
    30
    Dependencies = []
tw@2731
    31
tw@2731
    32
    # some common strings
tw@2731
    33
    PARENT_PREFIX = wikiutil.PARENT_PREFIX
tw@2731
    34
    punct_pattern = re.escape(u'''"\'}]|:,.)?!''')
tw@2731
    35
    attachment_schemas = ["attachment", "inline", "drawing", ]
tw@2731
    36
    url_schemas = ['http', 'https', 'ftp', 'wiki', 'mailto', 'nntp', 'news',
tw@2731
    37
                   'telnet', 'file', 'irc', 'ircs',
tw@2731
    38
                   'webcal', 'ed2k', 'xmpp', 'rootz',
tw@2731
    39
                  ]
tw@2731
    40
    url_pattern = u'|'.join(url_schemas + attachment_schemas)
tw@2731
    41
tw@2731
    42
    # some common rules
tw@2731
    43
    word_rule = ur'(?:(?<![%(u)s%(l)s])|^)%(parent)s(?:%(subpages)s(?:[%(u)s][%(l)s]+){2,})+(?![%(u)s%(l)s]+)' % {
tw@2731
    44
        'u': config.chars_upper,
tw@2731
    45
        'l': config.chars_lower,
tw@2731
    46
        'subpages': wikiutil.CHILD_PREFIX + '?',
tw@2731
    47
        'parent': ur'(?:%s)?' % re.escape(PARENT_PREFIX),
tw@2731
    48
    }
tw@2731
    49
    url_rule = ur'%(url_guard)s(%(url)s)\:([^\s\<%(punct)s]|([%(punct)s][^\s\<%(punct)s]))+' % {
tw@2731
    50
        'url_guard': u'(^|(?<!\w))',
tw@2731
    51
        'url': url_pattern,
tw@2731
    52
        'punct': punct_pattern,
tw@2731
    53
    }
tw@2731
    54
tw@2731
    55
    ol_rule = ur"^\s+(?:[0-9]+|[aAiI])\.(?:#\d+)?\s"
tw@2731
    56
    dl_rule = ur"^\s+.*?::\s"
tw@2731
    57
tw@2731
    58
    config_smileys = dict([(key, None) for key in config.smileys])
tw@2731
    59
tw@2731
    60
    # the big, fat, ugly one ;)
tw@2731
    61
    formatting_rules = ur"""(?P<ent_numeric>&#(\d{1,5}|x[0-9a-fA-F]+);)
tw@2731
    62
(?:(?P<emph_ibb>'''''(?=[^']+'''))
tw@2731
    63
(?P<emph_ibi>'''''(?=[^']+''))
tw@2731
    64
(?P<emph_ib_or_bi>'{5}(?=[^']))
tw@2731
    65
(?P<emph>'{2,3})
tw@2731
    66
(?P<u>__)
tw@2731
    67
(?P<sup>\^.*?\^)
tw@2731
    68
(?P<sub>,,[^,]{1,40},,)
tw@2731
    69
(?P<tt>\{\{\{.*?\}\}\})
tw@2731
    70
(?P<processor>(\{\{\{(#!.*|\s*$)))
tw@2731
    71
(?P<pre>(\{\{\{ ?|\}\}\}))
tw@2731
    72
(?P<small>(\~- ?|-\~))
tw@2731
    73
(?P<big>(\~\+ ?|\+\~))
tw@2731
    74
(?P<strike>(--\(|\)--))
tw@2731
    75
(?P<rule>-{4,})
tw@2731
    76
(?P<comment>^\#\#.*$)
tw@2731
    77
(?P<macro>\[\[(%%(macronames)s)(?:\(.*?\))?\]\]))
tw@2731
    78
(?P<ol>%(ol_rule)s)
tw@2731
    79
(?P<dl>%(dl_rule)s)
tw@2731
    80
(?P<li>^\s+\*\s*)
tw@2731
    81
(?P<li_none>^\s+\.\s*)
tw@2731
    82
(?P<indent>^\s+)
tw@2731
    83
(?P<tableZ>\|\| $)
tw@2731
    84
(?P<table>(?:\|\|)+(?:<[^>]*?>)?(?!\|? $))
tw@2731
    85
(?P<heading>^\s*(?P<hmarker>=+)\s.*\s(?P=hmarker) $)
tw@2731
    86
(?P<interwiki>[A-Z][a-zA-Z]+\:[^\s'\"\:\<\|]([^\s%(punct)s]|([%(punct)s][^\s%(punct)s]))+)
tw@2731
    87
(?P<word>%(word_rule)s)
tw@2731
    88
(?P<url_bracket>\[((%(url)s)\:|#|\:)[^\s\]]+(\s[^\]]+)?\])
tw@2731
    89
(?P<url>%(url_rule)s)
tw@2731
    90
(?P<email>[-\w._+]+\@[\w-]+(\.[\w-]+)+)
tw@2731
    91
(?P<smiley>(?<=\s)(%(smiley)s)(?=\s))
tw@2731
    92
(?P<smileyA>^(%(smiley)s)(?=\s))
tw@2731
    93
(?P<ent_symbolic>&\w+;)
tw@2731
    94
(?P<ent>[<>&])
tw@2731
    95
(?P<wikiname_bracket>\[".*?"\])
tw@2731
    96
(?P<tt_bt>`.*?`)"""  % {
tw@2731
    97
tw@2731
    98
        'url': url_pattern,
tw@2731
    99
        'punct': punct_pattern,
tw@2731
   100
        'ol_rule': ol_rule,
tw@2731
   101
        'dl_rule': dl_rule,
tw@2731
   102
        'url_rule': url_rule,
tw@2731
   103
        'word_rule': word_rule,
tw@2731
   104
        'smiley': u'|'.join(map(re.escape, config_smileys.keys()))}
tw@2731
   105
tw@2731
   106
    # Don't start p before these
tw@2731
   107
    no_new_p_before = ("heading rule table tableZ tr td "
tw@2731
   108
                       "ul ol dl dt dd li li_none indent "
tw@2731
   109
                       "macro processor pre")
tw@2731
   110
    no_new_p_before = no_new_p_before.split()
tw@2731
   111
    no_new_p_before = dict(zip(no_new_p_before, [1] * len(no_new_p_before)))
tw@2731
   112
tw@2731
   113
    def __init__(self, raw, request, **kw):
tw@2731
   114
        self.raw = raw
tw@2731
   115
        self.request = request
tw@2731
   116
        self.form = request.form
tw@2731
   117
        self._ = request.getText
tw@2731
   118
        self.cfg = request.cfg
tw@2731
   119
        self.line_anchors = kw.get('line_anchors', True)
tw@2731
   120
        self.macro = None
tw@2731
   121
        self.start_line = kw.get('start_line', 0)
tw@2731
   122
tw@2731
   123
        self.is_em = 0
tw@2731
   124
        self.is_b = 0
tw@2731
   125
        self.is_u = 0
tw@2731
   126
        self.is_strike = 0
tw@2731
   127
        self.lineno = 0
tw@2731
   128
        self.in_list = 0 # between <ul/ol/dl> and </ul/ol/dl>
tw@2731
   129
        self.in_li = 0 # between <li> and </li>
tw@2731
   130
        self.in_dd = 0 # between <dd> and </dd>
tw@2731
   131
        self.in_pre = 0
tw@2731
   132
        self.in_table = 0
tw@2731
   133
        self.is_big = False
tw@2731
   134
        self.is_small = False
tw@2731
   135
        self.inhibit_p = 0 # if set, do not auto-create a <p>aragraph
tw@2731
   136
        self.titles = request._page_headings
tw@2731
   137
tw@2731
   138
        # holds the nesting level (in chars) of open lists
tw@2731
   139
        self.list_indents = []
tw@2731
   140
        self.list_types = []
tw@2731
   141
tw@2731
   142
        self.formatting_rules = self.formatting_rules % {'macronames': u'|'.join(wikimacro.getNames(self.cfg))}
tw@2731
   143
tw@2731
   144
    def _close_item(self, result):
tw@2731
   145
        #result.append("<!-- close item begin -->\n")
tw@2731
   146
        if self.in_table:
tw@2731
   147
            result.append(self.formatter.table(0))
tw@2731
   148
            self.in_table = 0
tw@2731
   149
        if self.in_li:
tw@2731
   150
            self.in_li = 0
tw@2731
   151
            if self.formatter.in_p:
tw@2731
   152
                result.append(self.formatter.paragraph(0))
tw@2731
   153
            result.append(self.formatter.listitem(0))
tw@2731
   154
        if self.in_dd:
tw@2731
   155
            self.in_dd = 0
tw@2731
   156
            if self.formatter.in_p:
tw@2731
   157
                result.append(self.formatter.paragraph(0))
tw@2731
   158
            result.append(self.formatter.definition_desc(0))
tw@2731
   159
        #result.append("<!-- close item end -->\n")
tw@2731
   160
tw@2731
   161
tw@2731
   162
    def interwiki(self, url_and_text, **kw):
tw@2731
   163
        # TODO: maybe support [wiki:Page http://wherever/image.png] ?
tw@2731
   164
        if len(url_and_text) == 1:
tw@2731
   165
            url = url_and_text[0]
tw@2731
   166
            text = None
tw@2731
   167
        else:
tw@2731
   168
            url, text = url_and_text
tw@2731
   169
tw@2731
   170
        # keep track of whether this is a self-reference, so links
tw@2731
   171
        # are always shown even the page doesn't exist.
tw@2731
   172
        is_self_reference = 0
tw@2731
   173
        url2 = url.lower()
tw@2731
   174
        if url2.startswith('wiki:self:'):
tw@2731
   175
            url = url[10:] # remove "wiki:self:"
tw@2731
   176
            is_self_reference = 1
tw@2731
   177
        elif url2.startswith('wiki:'):
tw@3119
   178
            url = url[5:] # remove "wiki:"
tw@2731
   179
tw@2731
   180
        tag, tail = wikiutil.split_wiki(url)
tw@2731
   181
        if text is None:
tw@2731
   182
            if tag:
tw@2731
   183
                text = tail
tw@2731
   184
            else:
tw@2731
   185
                text = url
tw@2731
   186
                url = ""
tw@2731
   187
        elif (url.startswith(wikiutil.CHILD_PREFIX) or # fancy link to subpage [wiki:/SubPage text]
tw@2731
   188
              is_self_reference or # [wiki:Self:LocalPage text] or [:LocalPage:text]
tw@2731
   189
              Page(self.request, url).exists()): # fancy link to local page [wiki:LocalPage text]
tw@2731
   190
            return self._word_repl(url, text)
tw@2731
   191
tw@2731
   192
        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, url)
tw@2731
   193
        href = wikiutil.join_wiki(wikiurl, wikitail)
tw@2731
   194
tw@2731
   195
        # check for image URL, and possibly return IMG tag
tw@2731
   196
        if not kw.get('pretty_url', 0) and wikiutil.isPicture(wikitail):
tw@2731
   197
            return self.formatter.image(src=href)
tw@2731
   198
tw@2731
   199
        # link to self?
tw@2731
   200
        if wikitag is None:
tw@2731
   201
            return self._word_repl(wikitail)
tw@2731
   202
tw@2731
   203
        return (self.formatter.interwikilink(1, tag, tail) +
tw@2731
   204
                self.formatter.text(text) +
tw@2731
   205
                self.formatter.interwikilink(0, tag, tail))
tw@2731
   206
tw@2731
   207
    def attachment(self, url_and_text, **kw):
tw@2731
   208
        """ This gets called on attachment URLs.
tw@2731
   209
        """
tw@2731
   210
        _ = self._
tw@2731
   211
        if len(url_and_text) == 1:
tw@2731
   212
            url = url_and_text[0]
tw@2731
   213
            text = None
tw@2731
   214
        else:
tw@2731
   215
            url, text = url_and_text
tw@2731
   216
tw@2731
   217
        inline = url[0] == 'i'
tw@2731
   218
        drawing = url[0] == 'd'
tw@2731
   219
        url = url.split(":", 1)[1]
tw@4569
   220
        url = wikiutil.url_unquote(url)
tw@2731
   221
        text = text or url
tw@2731
   222
tw@2731
   223
        from MoinMoin.action import AttachFile
tw@2731
   224
        if drawing:
tw@2731
   225
            return self.formatter.attachment_drawing(url, text)
tw@2731
   226
tw@2731
   227
        # check for image URL, and possibly return IMG tag
tw@2731
   228
        # (images are always inlined, just like for other URLs)
tw@2731
   229
        if not kw.get('pretty_url', 0) and wikiutil.isPicture(url):
tw@2731
   230
            return self.formatter.attachment_image(url)
tw@2731
   231
tw@2731
   232
        # inline the attachment
tw@2731
   233
        if inline:
tw@2731
   234
            return self.formatter.attachment_inlined(url, text)
tw@2731
   235
tw@2731
   236
        return self.formatter.attachment_link(url, text)
tw@2731
   237
tw@2731
   238
    def _u_repl(self, word):
tw@2731
   239
        """Handle underline."""
tw@2731
   240
        self.is_u = not self.is_u
tw@2731
   241
        return self.formatter.underline(self.is_u)
tw@2731
   242
tw@2731
   243
    def _strike_repl(self, word):
tw@2731
   244
        """Handle strikethrough."""
tw@2731
   245
        # XXX we don't really enforce the correct sequence --( ... )-- here
tw@2731
   246
        self.is_strike = not self.is_strike
tw@2731
   247
        return self.formatter.strike(self.is_strike)
tw@2731
   248
tw@2731
   249
    def _small_repl(self, word):
tw@2731
   250
        """Handle small."""
tw@2731
   251
        if word.strip() == '~-' and self.is_small:
tw@2731
   252
            return self.formatter.text(word)
tw@2731
   253
        if word.strip() == '-~' and not self.is_small:
tw@2731
   254
            return self.formatter.text(word)
tw@2731
   255
        self.is_small = not self.is_small
tw@2731
   256
        return self.formatter.small(self.is_small)
tw@2731
   257
tw@2731
   258
    def _big_repl(self, word):
tw@2731
   259
        """Handle big."""
tw@2731
   260
        if word.strip() == '~+' and self.is_big:
tw@2731
   261
            return self.formatter.text(word)
tw@2731
   262
        if word.strip() == '+~' and not self.is_big:
tw@2731
   263
            return self.formatter.text(word)
tw@2731
   264
        self.is_big = not self.is_big
tw@2731
   265
        return self.formatter.big(self.is_big)
tw@2731
   266
tw@2731
   267
    def _emph_repl(self, word):
tw@2731
   268
        """Handle emphasis, i.e. '' and '''."""
tw@2731
   269
        ##print "#", self.is_b, self.is_em, "#"
tw@2731
   270
        if len(word) == 3:
tw@2731
   271
            self.is_b = not self.is_b
tw@2731
   272
            if self.is_em and self.is_b:
tw@2731
   273
                self.is_b = 2
tw@2731
   274
            return self.formatter.strong(self.is_b)
tw@2731
   275
        else:
tw@2731
   276
            self.is_em = not self.is_em
tw@2731
   277
            if self.is_em and self.is_b:
tw@2731
   278
                self.is_em = 2
tw@2731
   279
            return self.formatter.emphasis(self.is_em)
tw@2731
   280
tw@2731
   281
    def _emph_ibb_repl(self, word):
tw@2731
   282
        """Handle mixed emphasis, i.e. ''''' followed by '''."""
tw@2731
   283
        self.is_b = not self.is_b
tw@2731
   284
        self.is_em = not self.is_em
tw@2731
   285
        if self.is_em and self.is_b:
tw@2731
   286
            self.is_b = 2
tw@2731
   287
        return self.formatter.emphasis(self.is_em) + self.formatter.strong(self.is_b)
tw@2731
   288
tw@2731
   289
    def _emph_ibi_repl(self, word):
tw@2731
   290
        """Handle mixed emphasis, i.e. ''''' followed by ''."""
tw@2731
   291
        self.is_b = not self.is_b
tw@2731
   292
        self.is_em = not self.is_em
tw@2731
   293
        if self.is_em and self.is_b:
tw@2731
   294
            self.is_em = 2
tw@2731
   295
        return self.formatter.strong(self.is_b) + self.formatter.emphasis(self.is_em)
tw@2731
   296
tw@2731
   297
    def _emph_ib_or_bi_repl(self, word):
tw@2731
   298
        """Handle mixed emphasis, exactly five '''''."""
tw@2731
   299
        ##print "*", self.is_b, self.is_em, "*"
tw@2731
   300
        b_before_em = self.is_b > self.is_em > 0
tw@2731
   301
        self.is_b = not self.is_b
tw@2731
   302
        self.is_em = not self.is_em
tw@2731
   303
        if b_before_em:
tw@2731
   304
            return self.formatter.strong(self.is_b) + self.formatter.emphasis(self.is_em)
tw@2731
   305
        else:
tw@2731
   306
            return self.formatter.emphasis(self.is_em) + self.formatter.strong(self.is_b)
tw@2731
   307
tw@2731
   308
tw@2731
   309
    def _sup_repl(self, word):
tw@2731
   310
        """Handle superscript."""
tw@2731
   311
        return self.formatter.sup(1) + \
tw@2731
   312
            self.formatter.text(word[1:-1]) + \
tw@2731
   313
            self.formatter.sup(0)
tw@2731
   314
tw@2731
   315
    def _sub_repl(self, word):
tw@2731
   316
        """Handle subscript."""
tw@2731
   317
        return self.formatter.sub(1) + \
tw@2731
   318
            self.formatter.text(word[2:-2]) + \
tw@2731
   319
            self.formatter.sub(0)
tw@2731
   320
tw@2731
   321
tw@2731
   322
    def _rule_repl(self, word):
tw@2731
   323
        """Handle sequences of dashes."""
tw@2731
   324
        result = self._undent() + self._closeP()
tw@2731
   325
        if len(word) <= 4:
tw@2731
   326
            result = result + self.formatter.rule()
tw@2731
   327
        else:
tw@2731
   328
            # Create variable rule size 1 - 6. Actual size defined in css.
tw@2731
   329
            size = min(len(word), 10) - 4
tw@2731
   330
            result = result + self.formatter.rule(size)
tw@2731
   331
        return result
tw@2731
   332
tw@2731
   333
tw@2731
   334
    def _word_repl(self, word, text=None):
tw@2731
   335
        """Handle WikiNames."""
tw@2731
   336
tw@2731
   337
        # check for parent links
tw@2731
   338
        # !!! should use wikiutil.AbsPageName here, but setting `text`
tw@2731
   339
        # correctly prevents us from doing this for now
tw@2731
   340
        if word.startswith(wikiutil.PARENT_PREFIX):
tw@2731
   341
            if not text:
tw@2731
   342
                text = word
tw@2731
   343
            word = '/'.join(filter(None, self.formatter.page.page_name.split('/')[:-1] + [word[wikiutil.PARENT_PREFIX_LEN:]]))
tw@2731
   344
tw@2731
   345
        if not text:
tw@2731
   346
            # if a simple, self-referencing link, emit it as plain text
tw@2731
   347
            if word == self.formatter.page.page_name:
tw@2731
   348
                return self.formatter.text(word)
tw@2731
   349
            text = word
tw@2731
   350
        if word.startswith(wikiutil.CHILD_PREFIX):
tw@2731
   351
            word = self.formatter.page.page_name + '/' + word[wikiutil.CHILD_PREFIX_LEN:]
tw@2731
   352
tw@2731
   353
        # handle anchors
tw@2731
   354
        parts = word.split("#", 1)
tw@2731
   355
        anchor = ""
tw@2731
   356
        if len(parts) == 2:
tw@2731
   357
            word, anchor = parts
tw@2731
   358
tw@2731
   359
        return (self.formatter.pagelink(1, word, anchor=anchor) +
tw@2731
   360
                self.formatter.text(text) +
tw@2731
   361
                self.formatter.pagelink(0, word))
tw@2731
   362
tw@2731
   363
    def _notword_repl(self, word):
tw@2731
   364
        """Handle !NotWikiNames."""
tw@2731
   365
        return self.formatter.nowikiword(word[1:])
tw@2731
   366
tw@2731
   367
    def _interwiki_repl(self, word):
tw@2731
   368
        """Handle InterWiki links."""
tw@2731
   369
        wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, word)
tw@2731
   370
        if wikitag_bad:
tw@2731
   371
            return self.formatter.text(word)
tw@2731
   372
        else:
tw@2731
   373
            return self.interwiki(["wiki:" + word])
tw@2731
   374
tw@2731
   375
tw@2731
   376
    def _url_repl(self, word):
tw@2731
   377
        """Handle literal URLs including inline images."""
tw@2731
   378
        scheme = word.split(":", 1)[0]
tw@2731
   379
tw@2731
   380
        if scheme == "wiki":
tw@2731
   381
            return self.interwiki([word])
tw@2731
   382
        if scheme in self.attachment_schemas:
tw@2731
   383
            return self.attachment([word])
tw@2731
   384
tw@2731
   385
        if wikiutil.isPicture(word):
tw@2731
   386
            word = wikiutil.mapURL(self.request, word)
tw@2731
   387
            # Get image name http://here.com/dir/image.gif -> image
tw@2731
   388
            name = word.split('/')[-1]
tw@2731
   389
            name = ''.join(name.split('.')[:-1])
tw@2731
   390
            return self.formatter.image(src=word, alt=name)
tw@2731
   391
        else:
tw@2731
   392
            return (self.formatter.url(1, word, css=scheme) +
tw@2731
   393
                    self.formatter.text(word) +
tw@2731
   394
                    self.formatter.url(0))
tw@2731
   395
tw@2731
   396
tw@2731
   397
    def _wikiname_bracket_repl(self, word):
tw@2731
   398
        """Handle special-char wikinames."""
tw@2731
   399
        wikiname = word[2:-2]
tw@2731
   400
        if wikiname:
tw@2731
   401
            return self._word_repl(wikiname)
tw@2731
   402
        else:
tw@2731
   403
            return self.formatter.text(word)
tw@2731
   404
tw@2731
   405
tw@2731
   406
    def _url_bracket_repl(self, word):
tw@2731
   407
        """Handle bracketed URLs."""
tw@2731
   408
tw@2731
   409
        # Local extended link?
tw@2731
   410
        if word[1] == ':':
tw@2731
   411
            words = word[2:-1].split(':', 1)
tw@2731
   412
            if len(words) == 1:
tw@2731
   413
                words = words * 2
tw@2731
   414
            words[0] = 'wiki:Self:%s' % words[0]
tw@2731
   415
            return self.interwiki(words, pretty_url=1)
tw@2731
   416
            #return self._word_repl(words[0], words[1])
tw@2731
   417
tw@2731
   418
        # Traditional split on space
tw@2731
   419
        words = word[1:-1].split(None, 1)
tw@2731
   420
        if len(words) == 1:
tw@2731
   421
            words = words * 2
tw@2731
   422
tw@2731
   423
        if words[0][0] == '#':
tw@2731
   424
            # anchor link
tw@2731
   425
            return (self.formatter.url(1, words[0]) +
tw@2731
   426
                    self.formatter.text(words[1]) +
tw@2731
   427
                    self.formatter.url(0))
tw@2731
   428
tw@2731
   429
        scheme = words[0].split(":", 1)[0]
tw@2731
   430
        if scheme == "wiki":
tw@2731
   431
            return self.interwiki(words, pretty_url=1)
tw@2731
   432
        if scheme in self.attachment_schemas:
tw@2731
   433
            return self.attachment(words, pretty_url=1)
tw@2731
   434
tw@2731
   435
        if wikiutil.isPicture(words[1]) and re.match(self.url_rule, words[1]):
tw@3270
   436
            return (self.formatter.url(1, words[0], css='external') +
tw@2731
   437
                    self.formatter.image(title=words[0], alt=words[0], src=words[1]) +
tw@2731
   438
                    self.formatter.url(0))
tw@2731
   439
        else:
tw@3270
   440
            return (self.formatter.url(1, words[0], css=scheme) +
tw@2731
   441
                    self.formatter.text(words[1]) +
tw@2731
   442
                    self.formatter.url(0))
tw@2731
   443
tw@2731
   444
tw@2731
   445
    def _email_repl(self, word):
tw@2731
   446
        """Handle email addresses (without a leading mailto:)."""
tw@2731
   447
        return (self.formatter.url(1, "mailto:" + word, css='mailto') +
tw@2731
   448
                self.formatter.text(word) +
tw@2731
   449
                self.formatter.url(0))
tw@2731
   450
tw@2731
   451
tw@2731
   452
    def _ent_repl(self, word):
tw@2731
   453
        """Handle SGML entities."""
tw@2731
   454
        return self.formatter.text(word)
tw@2731
   455
        #return {'&': '&amp;',
tw@2731
   456
        #        '<': '&lt;',
tw@2731
   457
        #        '>': '&gt;'}[word]
tw@2731
   458
tw@2731
   459
    def _ent_numeric_repl(self, word):
tw@2731
   460
        """Handle numeric (decimal and hexadecimal) SGML entities."""
tw@2731
   461
        return self.formatter.rawHTML(word)
tw@2731
   462
tw@2731
   463
    def _ent_symbolic_repl(self, word):
tw@2731
   464
        """Handle symbolic SGML entities."""
tw@2731
   465
        return self.formatter.rawHTML(word)
tw@2731
   466
tw@2731
   467
    def _indent_repl(self, match):
tw@2731
   468
        """Handle pure indentation (no - * 1. markup)."""
tw@2731
   469
        result = []
tw@2731
   470
        if not (self.in_li or self.in_dd):
tw@2731
   471
            self._close_item(result)
tw@2731
   472
            self.in_li = 1
tw@2731
   473
            css_class = None
tw@2731
   474
            if self.line_was_empty and not self.first_list_item:
tw@2731
   475
                css_class = 'gap'
tw@2731
   476
            result.append(self.formatter.listitem(1, css_class=css_class, style="list-style-type:none"))
tw@2731
   477
        return ''.join(result)
tw@2731
   478
tw@2731
   479
    def _li_none_repl(self, match):
tw@2731
   480
        """Handle type=none (" .") lists."""
tw@2731
   481
        result = []
tw@2731
   482
        self._close_item(result)
tw@2731
   483
        self.in_li = 1
tw@2731
   484
        css_class = None
tw@2731
   485
        if self.line_was_empty and not self.first_list_item:
tw@2731
   486
            css_class = 'gap'
tw@2731
   487
        result.append(self.formatter.listitem(1, css_class=css_class, style="list-style-type:none"))
tw@2731
   488
        return ''.join(result)
tw@2731
   489
tw@2731
   490
    def _li_repl(self, match):
tw@2731
   491
        """Handle bullet (" *") lists."""
tw@2731
   492
        result = []
tw@2731
   493
        self._close_item(result)
tw@2731
   494
        self.in_li = 1
tw@2731
   495
        css_class = None
tw@2731
   496
        if self.line_was_empty and not self.first_list_item:
tw@2731
   497
            css_class = 'gap'
tw@2731
   498
        result.append(self.formatter.listitem(1, css_class=css_class))
tw@2731
   499
        return ''.join(result)
tw@2731
   500
tw@2731
   501
    def _ol_repl(self, match):
tw@2731
   502
        """Handle numbered lists."""
tw@2731
   503
        return self._li_repl(match)
tw@2731
   504
tw@2731
   505
    def _dl_repl(self, match):
tw@2731
   506
        """Handle definition lists."""
tw@2731
   507
        result = []
tw@2731
   508
        self._close_item(result)
tw@2731
   509
        self.in_dd = 1
tw@2731
   510
        result.extend([
tw@2731
   511
            self.formatter.definition_term(1),
tw@2731
   512
            self.formatter.text(match[1:-3].lstrip(' ')),
tw@2731
   513
            self.formatter.definition_term(0),
tw@2731
   514
            self.formatter.definition_desc(1),
tw@2731
   515
        ])
tw@2731
   516
        return ''.join(result)
tw@2731
   517
tw@2731
   518
tw@2731
   519
    def _indent_level(self):
tw@2731
   520
        """Return current char-wise indent level."""
tw@2731
   521
        return len(self.list_indents) and self.list_indents[-1]
tw@2731
   522
tw@2731
   523
tw@2731
   524
    def _indent_to(self, new_level, list_type, numtype, numstart):
tw@2731
   525
        """Close and open lists."""
tw@2731
   526
        open = []   # don't make one out of these two statements!
tw@2731
   527
        close = []
tw@2731
   528
tw@2731
   529
        if self._indent_level() != new_level and self.in_table:
tw@2731
   530
            close.append(self.formatter.table(0))
tw@2731
   531
            self.in_table = 0
tw@2731
   532
tw@2731
   533
        while self._indent_level() > new_level:
tw@2731
   534
            self._close_item(close)
tw@2731
   535
            if self.list_types[-1] == 'ol':
tw@2731
   536
                tag = self.formatter.number_list(0)
tw@2731
   537
            elif self.list_types[-1] == 'dl':
tw@2731
   538
                tag = self.formatter.definition_list(0)
tw@2731
   539
            else:
tw@2731
   540
                tag = self.formatter.bullet_list(0)
tw@2731
   541
            close.append(tag)
tw@2731
   542
tw@2731
   543
            del self.list_indents[-1]
tw@2731
   544
            del self.list_types[-1]
tw@2731
   545
tw@2731
   546
            if self.list_types: # we are still in a list
tw@2731
   547
                if self.list_types[-1] == 'dl':
tw@2731
   548
                    self.in_dd = 1
tw@2731
   549
                else:
tw@2731
   550
                    self.in_li = 1
tw@2731
   551
tw@2731
   552
        # Open new list, if necessary
tw@2731
   553
        if self._indent_level() < new_level:
tw@2731
   554
            self.list_indents.append(new_level)
tw@2731
   555
            self.list_types.append(list_type)
tw@2731
   556
tw@2731
   557
            if self.formatter.in_p:
tw@2731
   558
                close.append(self.formatter.paragraph(0))
tw@2731
   559
tw@2731
   560
            if list_type == 'ol':
tw@2731
   561
                tag = self.formatter.number_list(1, numtype, numstart)
tw@2731
   562
            elif list_type == 'dl':
tw@2731
   563
                tag = self.formatter.definition_list(1)
tw@2731
   564
            else:
tw@2731
   565
                tag = self.formatter.bullet_list(1)
tw@2731
   566
            open.append(tag)
tw@2731
   567
tw@2731
   568
            self.first_list_item = 1
tw@2731
   569
            self.in_li = 0
tw@2731
   570
            self.in_dd = 0
tw@2731
   571
tw@2731
   572
        # If list level changes, close an open table
tw@2731
   573
        if self.in_table and (open or close):
tw@2731
   574
            close[0:0] = [self.formatter.table(0)]
tw@2731
   575
            self.in_table = 0
tw@2731
   576
tw@2731
   577
        self.in_list = self.list_types != []
tw@2731
   578
        return ''.join(close) + ''.join(open)
tw@2731
   579
tw@2731
   580
tw@2731
   581
    def _undent(self):
tw@2731
   582
        """Close all open lists."""
tw@2731
   583
        result = []
tw@2731
   584
        #result.append("<!-- _undent start -->\n")
tw@2731
   585
        self._close_item(result)
tw@2731
   586
        for type in self.list_types[::-1]:
tw@2731
   587
            if type == 'ol':
tw@2731
   588
                result.append(self.formatter.number_list(0))
tw@2731
   589
            elif type == 'dl':
tw@2731
   590
                result.append(self.formatter.definition_list(0))
tw@2731
   591
            else:
tw@2731
   592
                result.append(self.formatter.bullet_list(0))
tw@2731
   593
        #result.append("<!-- _undent end -->\n")
tw@2731
   594
        self.list_indents = []
tw@2731
   595
        self.list_types = []
tw@2731
   596
        return ''.join(result)
tw@2731
   597
tw@2731
   598
tw@2731
   599
    def _tt_repl(self, word):
tw@2731
   600
        """Handle inline code."""
tw@2731
   601
        return self.formatter.code(1) + \
tw@2731
   602
            self.formatter.text(word[3:-3]) + \
tw@2731
   603
            self.formatter.code(0)
tw@2731
   604
tw@2731
   605
tw@2731
   606
    def _tt_bt_repl(self, word):
tw@2731
   607
        """Handle backticked inline code."""
tw@2731
   608
        # if len(word) == 2: return "" // removed for FCK editor
tw@2731
   609
        return self.formatter.code(1, css="backtick") + \
tw@2731
   610
            self.formatter.text(word[1:-1]) + \
tw@2731
   611
            self.formatter.code(0)
tw@2731
   612
tw@2731
   613
tw@2731
   614
    def _getTableAttrs(self, attrdef):
tw@2731
   615
        # skip "|" and initial "<"
tw@2731
   616
        while attrdef and attrdef[0] == "|":
tw@2731
   617
            attrdef = attrdef[1:]
tw@2731
   618
        if not attrdef or attrdef[0] != "<":
tw@2731
   619
            return {}, ''
tw@2731
   620
        attrdef = attrdef[1:]
tw@2731
   621
tw@2731
   622
        # extension for special table markup
tw@2731
   623
        def table_extension(key, parser, attrs, wiki_parser=self):
tw@2731
   624
            """ returns: tuple (found_flag, msg)
tw@2731
   625
                found_flag: whether we found something and were able to process it here
tw@2731
   626
                  true for special stuff like 100% or - or #AABBCC
tw@2731
   627
                  false for style xxx="yyy" attributes
tw@2731
   628
                msg: "" or an error msg
tw@2731
   629
            """
tw@2731
   630
            _ = wiki_parser._
tw@2731
   631
            found = False
tw@2731
   632
            msg = ''
tw@2731
   633
            if key[0] in "0123456789":
tw@2731
   634
                token = parser.get_token()
tw@2731
   635
                if token != '%':
tw@2731
   636
                    wanted = '%'
tw@2731
   637
                    msg = _('Expected "%(wanted)s" after "%(key)s", got "%(token)s"') % {
tw@2731
   638
                        'wanted': wanted, 'key': key, 'token': token}
tw@2731
   639
                else:
tw@2731
   640
                    try:
tw@2731
   641
                        dummy = int(key)
tw@2731
   642
                    except ValueError:
tw@2731
   643
                        msg = _('Expected an integer "%(key)s" before "%(token)s"') % {
tw@2731
   644
                            'key': key, 'token': token}
tw@2731
   645
                    else:
tw@2731
   646
                        found = True
tw@2731
   647
                        attrs['width'] = '"%s%%"' % key
tw@2731
   648
            elif key == '-':
tw@2731
   649
                arg = parser.get_token()
tw@2731
   650
                try:
tw@2731
   651
                    dummy = int(arg)
tw@2731
   652
                except ValueError:
tw@2731
   653
                    msg = _('Expected an integer "%(arg)s" after "%(key)s"') % {
tw@2731
   654
                        'arg': arg, 'key': key}
tw@2731
   655
                else:
tw@2731
   656
                    found = True
tw@2731
   657
                    attrs['colspan'] = '"%s"' % arg
tw@2731
   658
            elif key == '|':
tw@2731
   659
                arg = parser.get_token()
tw@2731
   660
                try:
tw@2731
   661
                    dummy = int(arg)
tw@2731
   662
                except ValueError:
tw@2731
   663
                    msg = _('Expected an integer "%(arg)s" after "%(key)s"') % {
tw@2731
   664
                        'arg': arg, 'key': key}
tw@2731
   665
                else:
tw@2731
   666
                    found = True
tw@2731
   667
                    attrs['rowspan'] = '"%s"' % arg
tw@2731
   668
            elif key == '(':
tw@2731
   669
                found = True
tw@2731
   670
                attrs['align'] = '"left"'
tw@2731
   671
            elif key == ':':
tw@2731
   672
                found = True
tw@2731
   673
                attrs['align'] = '"center"'
tw@2731
   674
            elif key == ')':
tw@2731
   675
                found = True
tw@2731
   676
                attrs['align'] = '"right"'
tw@2731
   677
            elif key == '^':
tw@2731
   678
                found = True
tw@2731
   679
                attrs['valign'] = '"top"'
tw@2731
   680
            elif key == 'v':
tw@2731
   681
                found = True
tw@2731
   682
                attrs['valign'] = '"bottom"'
tw@2731
   683
            elif key == '#':
tw@2731
   684
                arg = parser.get_token()
tw@2731
   685
                try:
tw@2731
   686
                    if len(arg) != 6: raise ValueError
tw@2731
   687
                    dummy = int(arg, 16)
tw@2731
   688
                except ValueError:
tw@2731
   689
                    msg = _('Expected a color value "%(arg)s" after "%(key)s"') % {
tw@2731
   690
                        'arg': arg, 'key': key}
tw@2731
   691
                else:
tw@2731
   692
                    found = True
tw@2731
   693
                    attrs['bgcolor'] = '"#%s"' % arg
tw@2731
   694
            return found, self.formatter.rawHTML(msg)
tw@2731
   695
tw@2731
   696
        # scan attributes
tw@2731
   697
        attr, msg = wikiutil.parseAttributes(self.request, attrdef, '>', table_extension)
tw@2731
   698
        if msg:
tw@2731
   699
            msg = '<strong class="highlight">%s</strong>' % msg
tw@2731
   700
        return attr, msg
tw@2731
   701
tw@2731
   702
    def _tableZ_repl(self, word):
tw@2731
   703
        """Handle table row end."""
tw@2731
   704
        if self.in_table:
tw@2731
   705
            result = ''
tw@2731
   706
            # REMOVED: check for self.in_li, p should always close
tw@2731
   707
            if self.formatter.in_p:
tw@2731
   708
                result = self.formatter.paragraph(0)
tw@2731
   709
            result += self.formatter.table_cell(0) + self.formatter.table_row(0)
tw@2731
   710
            return result
tw@2731
   711
        else:
tw@2731
   712
            return self.formatter.text(word)
tw@2731
   713
tw@2731
   714
    def _table_repl(self, word):
tw@2731
   715
        """Handle table cell separator."""
tw@2731
   716
        if self.in_table:
tw@2731
   717
            result = []
tw@2731
   718
            # check for attributes
tw@2731
   719
            attrs, attrerr = self._getTableAttrs(word)
tw@2731
   720
tw@2731
   721
            # start the table row?
tw@2731
   722
            if self.table_rowstart:
tw@2731
   723
                self.table_rowstart = 0
tw@2731
   724
                result.append(self.formatter.table_row(1, attrs))
tw@2731
   725
            else:
tw@2731
   726
                # Close table cell, first closing open p
tw@2731
   727
                # REMOVED check for self.in_li, paragraph should close always!
tw@2731
   728
                if self.formatter.in_p:
tw@2731
   729
                    result.append(self.formatter.paragraph(0))
tw@2731
   730
                result.append(self.formatter.table_cell(0))
tw@2731
   731
tw@2731
   732
            # check for adjacent cell markers
tw@2731
   733
            if word.count("|") > 2:
tw@2731
   734
                if not attrs.has_key('align') and \
tw@2731
   735
                   not (attrs.has_key('style') and 'text-align' in attrs['style'].lower()):
tw@2731
   736
                    # add center alignment if we don't have some alignment already
tw@2731
   737
                    attrs['align'] = '"center"'
tw@2731
   738
                if not attrs.has_key('colspan'):
tw@2731
   739
                    attrs['colspan'] = '"%d"' % (word.count("|")/2)
tw@2731
   740
tw@2731
   741
            # return the complete cell markup
tw@2731
   742
            result.append(self.formatter.table_cell(1, attrs) + attrerr)
tw@2731
   743
            result.append(self._line_anchordef())
tw@2731
   744
            return ''.join(result)
tw@2731
   745
        else:
tw@2731
   746
            return self.formatter.text(word)
tw@2731
   747
tw@2731
   748
tw@2731
   749
    def _heading_repl(self, word):
tw@2731
   750
        """Handle section headings."""
tw@4363
   751
        from MoinMoin.support.python_compatibility import hash_new
tw@2731
   752
tw@2731
   753
        h = word.strip()
tw@2731
   754
        level = 1
tw@2731
   755
        while h[level:level+1] == '=':
tw@2731
   756
            level += 1
tw@2731
   757
        depth = min(5, level)
tw@2731
   758
tw@2731
   759
        # this is needed for Included pages
tw@2731
   760
        # TODO but it might still result in unpredictable results
tw@2731
   761
        # when included the same page multiple times
tw@2731
   762
        title_text = h[level:-level].strip()
tw@2731
   763
        pntt = self.formatter.page.page_name + title_text
tw@2731
   764
        self.titles.setdefault(pntt, 0)
tw@2731
   765
        self.titles[pntt] += 1
tw@2731
   766
tw@2731
   767
        unique_id = ''
tw@2731
   768
        if self.titles[pntt] > 1:
tw@2731
   769
            unique_id = '-%d' % self.titles[pntt]
tw@2731
   770
        result = self._closeP()
tw@4363
   771
        result += self.formatter.heading(1, depth, id="head-"+hash_new('sha1', pntt.encode(config.charset)).hexdigest()+unique_id)
tw@2731
   772
tw@2731
   773
        return (result + self.formatter.text(title_text) +
tw@2731
   774
                self.formatter.heading(0, depth))
tw@2731
   775
tw@2731
   776
    def _processor_repl(self, word):
tw@2731
   777
        """Handle processed code displays."""
tw@2731
   778
        if word[:3] == '{{{':
tw@2731
   779
            word = word[3:]
tw@2731
   780
tw@2731
   781
        self.processor = None
tw@2731
   782
        self.processor_name = None
tw@2731
   783
        self.processor_is_parser = 0
tw@2731
   784
        s_word = word.strip()
tw@2731
   785
        if s_word == '#!':
tw@2731
   786
            # empty bang paths lead to a normal code display
tw@2731
   787
            # can be used to escape real, non-empty bang paths
tw@2731
   788
            word = ''
tw@2731
   789
            self.in_pre = 3
tw@2731
   790
            return self._closeP() + self.formatter.preformatted(1)
tw@2731
   791
        elif s_word[:2] == '#!':
tw@2731
   792
            # First try to find a processor for this (will go away in 2.0)
tw@2731
   793
            processor_name = s_word[2:].split()[0]
tw@2731
   794
            self.setProcessor(processor_name)
tw@2731
   795
tw@2731
   796
        if self.processor:
tw@2731
   797
            self.processor_name = processor_name
tw@2731
   798
            self.in_pre = 2
tw@2731
   799
            self.colorize_lines = [word]
tw@2731
   800
            return ''
tw@2731
   801
        elif s_word:
tw@2731
   802
            self.in_pre = 3
tw@2731
   803
            return self._closeP() + self.formatter.preformatted(1) + \
tw@2731
   804
                   self.formatter.text(s_word + ' (-)')
tw@2731
   805
        else:
tw@2731
   806
            self.in_pre = 1
tw@2731
   807
            return ''
tw@2731
   808
tw@2731
   809
    def _pre_repl(self, word):
tw@2731
   810
        """Handle code displays."""
tw@2731
   811
        word = word.strip()
tw@2731
   812
        if word == '{{{' and not self.in_pre:
tw@2731
   813
            self.in_pre = 3
tw@2731
   814
            return self._closeP() + self.formatter.preformatted(self.in_pre)
tw@2731
   815
        elif word == '}}}' and self.in_pre:
tw@2731
   816
            self.in_pre = 0
tw@2731
   817
            self.inhibit_p = 0
tw@2731
   818
            return self.formatter.preformatted(self.in_pre)
tw@2731
   819
        return self.formatter.text(word)
tw@2731
   820
tw@2731
   821
tw@2731
   822
    def _smiley_repl(self, word):
tw@2731
   823
        """Handle smileys."""
tw@2731
   824
        return self.formatter.smiley(word)
tw@2731
   825
tw@2731
   826
    _smileyA_repl = _smiley_repl
tw@2731
   827
tw@2731
   828
tw@2731
   829
    def _comment_repl(self, word):
tw@2731
   830
        # if we are in a paragraph, we must close it so that normal text following
tw@2731
   831
        # in the line below the comment will reopen a new paragraph.
tw@2731
   832
        if self.formatter.in_p:
tw@2731
   833
            self.formatter.paragraph(0)
tw@2731
   834
        self.line_is_empty = 1 # markup following comment lines treats them as if they were empty
tw@2731
   835
        return self.formatter.comment(word)
tw@2731
   836
tw@2731
   837
    def _closeP(self):
tw@2731
   838
        if self.formatter.in_p:
tw@2731
   839
            return self.formatter.paragraph(0)
tw@2731
   840
        return ''
tw@2731
   841
tw@2731
   842
    def _macro_repl(self, word):
tw@2731
   843
        """Handle macros ([[macroname]])."""
tw@2731
   844
        macro_name = word[2:-2]
tw@2731
   845
        self.inhibit_p = 0 # 1 fixes UserPreferences, 0 fixes paragraph formatting for macros
tw@2731
   846
tw@2731
   847
        # check for arguments
tw@2731
   848
        args = None
tw@2731
   849
        if macro_name.count("("):
tw@2731
   850
            macro_name, args = macro_name.split('(', 1)
tw@2731
   851
            args = args[:-1]
tw@2731
   852
tw@2731
   853
        # create macro instance
tw@2731
   854
        if self.macro is None:
tw@2731
   855
            self.macro = wikimacro.Macro(self)
tw@2731
   856
        return self.formatter.macro(self.macro, macro_name, args)
tw@2731
   857
tw@2731
   858
    def scan(self, scan_re, line):
tw@2731
   859
        """ Scans one line
tw@2731
   860
tw@2731
   861
        Append text before match, invoke replace() with match, and add text after match.
tw@2731
   862
        """
tw@2731
   863
        result = []
tw@2731
   864
        lastpos = 0
tw@2731
   865
tw@2731
   866
        ###result.append(u'<span class="info">[scan: <tt>"%s"</tt>]</span>' % line)
tw@2731
   867
tw@2731
   868
        for match in scan_re.finditer(line):
tw@2731
   869
            # Add text before the match
tw@2731
   870
            if lastpos < match.start():
tw@2731
   871
tw@2731
   872
                ###result.append(u'<span class="info">[add text before match: <tt>"%s"</tt>]</span>' % line[lastpos:match.start()])
tw@2731
   873
tw@2731
   874
                if not (self.inhibit_p or self.in_pre or self.formatter.in_p):
tw@2731
   875
                    result.append(self.formatter.paragraph(1, css_class="line862"))
tw@2731
   876
                result.append(self.formatter.text(line[lastpos:match.start()]))
tw@2731
   877
tw@2731
   878
            # Replace match with markup
tw@2731
   879
            if not (self.inhibit_p or self.in_pre or self.formatter.in_p or
tw@2731
   880
                    self.in_table or self.in_list):
tw@2731
   881
                result.append(self.formatter.paragraph(1, css_class="line867"))
tw@2731
   882
            result.append(self.replace(match))
tw@2731
   883
            lastpos = match.end()
tw@2731
   884
tw@2731
   885
        ###result.append('<span class="info">[no match, add rest: <tt>"%s"<tt>]</span>' % line[lastpos:])
tw@2731
   886
tw@2731
   887
        # Add paragraph with the remainder of the line
tw@2731
   888
        if not (self.in_pre or self.in_li or self.in_dd or self.inhibit_p or
tw@2731
   889
                self.formatter.in_p) and lastpos < len(line):
tw@2731
   890
            result.append(self.formatter.paragraph(1, css_class="line874"))
tw@2731
   891
        result.append(self.formatter.text(line[lastpos:]))
tw@2731
   892
        return u''.join(result)
tw@2731
   893
tw@2731
   894
    def replace(self, match):
tw@2731
   895
        """ Replace match using type name """
tw@2731
   896
        result = []
tw@2731
   897
        for type, hit in match.groupdict().items():
tw@2731
   898
            if hit is not None and type != "hmarker":
tw@2731
   899
tw@2731
   900
                ###result.append(u'<span class="info">[replace: %s: "%s"]</span>' % (type, hit))
tw@2731
   901
                if self.in_pre and type not in ['pre', 'ent']:
tw@2731
   902
                    return self.formatter.text(hit)
tw@2731
   903
                else:
tw@2731
   904
                    # Open p for certain types
tw@2731
   905
                    if not (self.inhibit_p or self.formatter.in_p
tw@2731
   906
                            or self.in_pre or (type in self.no_new_p_before)):
tw@2731
   907
                        result.append(self.formatter.paragraph(1, css_class="line891"))
tw@2731
   908
tw@2731
   909
                    # Get replace method and replece hit
tw@2731
   910
                    replace = getattr(self, '_' + type + '_repl')
tw@2731
   911
                    result.append(replace(hit))
tw@2731
   912
                    return ''.join(result)
tw@2731
   913
        else:
tw@2731
   914
            # We should never get here
tw@2731
   915
            import pprint
tw@2731
   916
            raise Exception("Can't handle match " + `match`
tw@2731
   917
                + "\n" + pprint.pformat(match.groupdict())
tw@2731
   918
                + "\n" + pprint.pformat(match.groups()) )
tw@2731
   919
tw@2731
   920
        return ""
tw@2731
   921
tw@2731
   922
    def _line_anchordef(self):
tw@2731
   923
        if self.line_anchors and not self.line_anchor_printed:
tw@2731
   924
            self.line_anchor_printed = 1
tw@2731
   925
            return self.formatter.line_anchordef(self.lineno)
tw@2731
   926
        else:
tw@2731
   927
            return ''
tw@2731
   928
tw@2731
   929
    def format(self, formatter):
tw@2731
   930
        """ For each line, scan through looking for magic
tw@2731
   931
            strings, outputting verbatim any intervening text.
tw@2731
   932
        """
tw@2731
   933
        self.formatter = formatter
tw@2731
   934
        self.hilite_re = self.formatter.page.hilite_re
tw@2731
   935
tw@2731
   936
        # prepare regex patterns
tw@2731
   937
        rules = self.formatting_rules.replace('\n', '|')
tw@2731
   938
        if self.cfg.bang_meta:
tw@2731
   939
            rules = ur'(?P<notword>!%(word_rule)s)|%(rules)s' % {
tw@2731
   940
                'word_rule': self.word_rule,
tw@2731
   941
                'rules': rules,
tw@2731
   942
            }
tw@2731
   943
        self.request.clock.start('compile_huge_and_ugly')
tw@2731
   944
        scan_re = re.compile(rules, re.UNICODE)
tw@2731
   945
        number_re = re.compile(self.ol_rule, re.UNICODE)
tw@2731
   946
        term_re = re.compile(self.dl_rule, re.UNICODE)
tw@2731
   947
        indent_re = re.compile("^\s*", re.UNICODE)
tw@2731
   948
        eol_re = re.compile(r'\r?\n', re.UNICODE)
tw@2731
   949
        self.request.clock.stop('compile_huge_and_ugly')
tw@2731
   950
tw@2731
   951
        # get text and replace TABs
tw@2731
   952
        rawtext = self.raw.expandtabs()
tw@2731
   953
tw@2731
   954
        # go through the lines
tw@2731
   955
        self.lineno = self.start_line
tw@2731
   956
        self.lines = eol_re.split(rawtext)
tw@2731
   957
        self.line_is_empty = 0
tw@2731
   958
tw@2731
   959
        self.in_processing_instructions = 1
tw@2731
   960
tw@2731
   961
        # Main loop
tw@2731
   962
        for line in self.lines:
tw@2731
   963
            self.lineno += 1
tw@2731
   964
            self.line_anchor_printed = 0
tw@2731
   965
            if not self.in_table:
tw@2731
   966
                self.request.write(self._line_anchordef())
tw@2731
   967
            self.table_rowstart = 1
tw@2731
   968
            self.line_was_empty = self.line_is_empty
tw@2731
   969
            self.line_is_empty = 0
tw@2731
   970
            self.first_list_item = 0
tw@2731
   971
            self.inhibit_p = 0
tw@2731
   972
tw@2731
   973
            # ignore processing instructions
tw@2731
   974
            if self.in_processing_instructions:
tw@2731
   975
                found = False
tw@2731
   976
                for pi in ("##", "#format", "#refresh", "#redirect", "#deprecated",
tw@2731
   977
                           "#pragma", "#form", "#acl", "#language"):
tw@2731
   978
                    if line.lower().startswith(pi):
tw@2731
   979
                        self.request.write(self.formatter.comment(line))
tw@2731
   980
                        found = True
tw@2731
   981
                        break
tw@2731
   982
                if not found:
tw@2731
   983
                    self.in_processing_instructions = 0
tw@2731
   984
                else:
tw@2731
   985
                    continue # do not parse this line
tw@2731
   986
            if self.in_pre:
tw@2731
   987
                # TODO: move this into function
tw@2731
   988
                # still looking for processing instructions
tw@2731
   989
                # TODO: use strings for pre state, not numbers
tw@2731
   990
                if self.in_pre == 1:
tw@2731
   991
                    self.processor = None
tw@2731
   992
                    self.processor_is_parser = 0
tw@2731
   993
                    processor_name = ''
tw@2731
   994
                    if (line.strip()[:2] == "#!"):
tw@2731
   995
                        processor_name = line.strip()[2:].split()[0]
tw@2731
   996
                        self.setProcessor(processor_name)
tw@2731
   997
tw@2731
   998
                    if self.processor:
tw@2731
   999
                        self.in_pre = 2
tw@2731
  1000
                        self.colorize_lines = [line]
tw@2731
  1001
                        self.processor_name = processor_name
tw@2731
  1002
                        continue
tw@2731
  1003
                    else:
tw@2731
  1004
                        self.request.write(self._closeP() +
tw@2731
  1005
                                           self.formatter.preformatted(1))
tw@2731
  1006
                        self.in_pre = 3
tw@2731
  1007
                if self.in_pre == 2:
tw@2731
  1008
                    # processing mode
tw@2731
  1009
                    endpos = line.find("}}}")
tw@2731
  1010
                    if endpos == -1:
tw@2731
  1011
                        self.colorize_lines.append(line)
tw@2731
  1012
                        continue
tw@2731
  1013
                    if line[:endpos]:
tw@2731
  1014
                        self.colorize_lines.append(line[:endpos])
tw@2731
  1015
tw@2731
  1016
                    # Close p before calling processor
tw@2731
  1017
                    # TODO: do we really need this?
tw@2731
  1018
                    self.request.write(self._closeP())
tw@2731
  1019
                    res = self.formatter.processor(self.processor_name,
tw@2731
  1020
                                                   self.colorize_lines,
tw@2731
  1021
                                                   self.processor_is_parser)
tw@2731
  1022
                    self.request.write(res)
tw@2731
  1023
                    del self.colorize_lines
tw@2731
  1024
                    self.in_pre = 0
tw@2731
  1025
                    self.processor = None
tw@2731
  1026
tw@2731
  1027
                    # send rest of line through regex machinery
tw@2731
  1028
                    line = line[endpos+3:]
tw@2731
  1029
                    if not line.strip(): # just in the case "}}} " when we only have blanks left...
tw@2731
  1030
                        continue
tw@2731
  1031
            else:
tw@2731
  1032
                # we don't have \n as whitespace any more
tw@2731
  1033
                # This is the space between lines we join to one paragraph
tw@2731
  1034
                line += ' '
tw@2731
  1035
tw@2731
  1036
                # Paragraph break on empty lines
tw@2731
  1037
                if not line.strip():
tw@2731
  1038
                    if self.in_table:
tw@2731
  1039
                        self.request.write(self.formatter.table(0))
tw@2731
  1040
                        self.request.write(self._line_anchordef())
tw@2731
  1041
                        self.in_table = 0
tw@2731
  1042
                    # CHANGE: removed check for not self.list_types
tw@2731
  1043
                    # p should close on every empty line
tw@2731
  1044
                    if self.formatter.in_p:
tw@2731
  1045
                        self.request.write(self.formatter.paragraph(0))
tw@2731
  1046
                    self.line_is_empty = 1
tw@2731
  1047
                    continue
tw@2731
  1048
tw@2731
  1049
                # Check indent level
tw@2731
  1050
                indent = indent_re.match(line)
tw@2731
  1051
                indlen = len(indent.group(0))
tw@2731
  1052
                indtype = "ul"
tw@2731
  1053
                numtype = None
tw@2731
  1054
                numstart = None
tw@2731
  1055
                if indlen:
tw@2731
  1056
                    match = number_re.match(line)
tw@2731
  1057
                    if match:
tw@2731
  1058
                        numtype, numstart = match.group(0).strip().split('.')
tw@2731
  1059
                        numtype = numtype[0]
tw@2731
  1060
tw@2731
  1061
                        if numstart and numstart[0] == "#":
tw@2731
  1062
                            numstart = int(numstart[1:])
tw@2731
  1063
                        else:
tw@2731
  1064
                            numstart = None
tw@2731
  1065
tw@2731
  1066
                        indtype = "ol"
tw@2731
  1067
                    else:
tw@2731
  1068
                        match = term_re.match(line)
tw@2731
  1069
                        if match:
tw@2731
  1070
                            indtype = "dl"
tw@2731
  1071
tw@2731
  1072
                # output proper indentation tags
tw@2731
  1073
                self.request.write(self._indent_to(indlen, indtype, numtype, numstart))
tw@2731
  1074
tw@2731
  1075
                # Table mode
tw@2731
  1076
                # TODO: move into function?
tw@2731
  1077
                if (not self.in_table and line[indlen:indlen + 2] == "||"
tw@2731
  1078
                    and line[-3:] == "|| " and len(line) >= 5 + indlen):
tw@2731
  1079
                    # Start table
tw@2731
  1080
                    if self.list_types and not self.in_li:
tw@2731
  1081
                        self.request.write(self.formatter.listitem(1, style="list-style-type:none"))
tw@2731
  1082
                        ## CHANGE: no automatic p on li
tw@2731
  1083
                        ##self.request.write(self.formatter.paragraph(1))
tw@2731
  1084
                        self.in_li = 1
tw@2731
  1085
tw@2731
  1086
                    # CHANGE: removed check for self.in_li
tw@2731
  1087
                    # paragraph should end before table, always!
tw@2731
  1088
                    if self.formatter.in_p:
tw@2731
  1089
                        self.request.write(self.formatter.paragraph(0))
tw@2731
  1090
                    attrs, attrerr = self._getTableAttrs(line[indlen+2:])
tw@2731
  1091
                    self.request.write(self.formatter.table(1, attrs) + attrerr)
tw@2731
  1092
                    self.in_table = True # self.lineno
tw@2731
  1093
                elif (self.in_table and not
tw@2731
  1094
                      # intra-table comments should not break a table
tw@2731
  1095
                      (line[:2] == "##" or
tw@2731
  1096
                       line[indlen:indlen + 2] == "||" and
tw@2731
  1097
                       line[-3:] == "|| " and
tw@2731
  1098
                       len(line) >= 5 + indlen)):
tw@2731
  1099
tw@2731
  1100
                    # Close table
tw@2731
  1101
                    self.request.write(self.formatter.table(0))
tw@2731
  1102
                    self.request.write(self._line_anchordef())
tw@2731
  1103
                    self.in_table = 0
tw@2731
  1104
tw@2731
  1105
            # Scan line, format and write
tw@2731
  1106
            formatted_line = self.scan(scan_re, line)
tw@2731
  1107
            self.request.write(formatted_line)
tw@2731
  1108
tw@2731
  1109
            if self.in_pre == 3:
tw@2731
  1110
                self.request.write(self.formatter.linebreak())
tw@2731
  1111
tw@2731
  1112
        # Close code displays, paragraphs, tables and open lists
tw@2731
  1113
        self.request.write(self._undent())
tw@2731
  1114
        if self.in_pre: self.request.write(self.formatter.preformatted(0))
tw@2731
  1115
        if self.formatter.in_p: self.request.write(self.formatter.paragraph(0))
tw@2731
  1116
        if self.in_table: self.request.write(self.formatter.table(0))
tw@2731
  1117
tw@2731
  1118
    # --------------------------------------------------------------------
tw@2731
  1119
    # Private helpers
tw@2731
  1120
tw@2731
  1121
    def setProcessor(self, name):
tw@2731
  1122
        """ Set processer to either processor or parser named 'name' """
tw@2731
  1123
        cfg = self.request.cfg
tw@2731
  1124
        try:
tw@2731
  1125
            self.processor = wikiutil.importPlugin(cfg, "processor", name,
tw@2731
  1126
                                                   "process")
tw@2731
  1127
            self.processor_is_parser = 0
tw@2731
  1128
        except wikiutil.PluginMissingError:
tw@2731
  1129
            try:
tw@2731
  1130
                self.processor = wikiutil.importPlugin(cfg, "parser", name,
tw@2731
  1131
                                                   "Parser")
tw@2731
  1132
                self.processor_is_parser = 1
tw@2731
  1133
            except wikiutil.PluginMissingError:
tw@2731
  1134
                self.processor = None
tw@2731
  1135
tw@2731
  1136