changeset 1143:432c676186ba

Merge with main.
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Mon, 07 Aug 2006 21:53:20 +0200
parents 0448272f0e5b (current diff) db56bd53fc32 (diff)
children 1b5093cfc607
files MoinMoin/parser/ParserBase.py
diffstat 20 files changed, 410 insertions(+), 391 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/action/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/action/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -29,9 +29,11 @@
 from MoinMoin import wikiutil
 from MoinMoin.Page import Page
 
-# create a list of extension actions from the subpackage directory
-extension_actions = pysupport.getPackageModules(__file__)
-modules = extension_actions
+# create a list of extension actions from the package directory
+modules = pysupport.getPackageModules(__file__)
+
+# builtin-stuff (see do_<name> below):
+names = ['show', 'recall', 'raw', 'format', 'content', 'print', 'refresh', 'goto', 'userform', ]
 
 class ActionBase:
     """ action base class with some generic stuff to inherit
@@ -284,15 +286,14 @@
     Page(request, pagename).send_page(request, msg=savemsg)
 
 # Dispatching ----------------------------------------------------------------
-import os
-
-def getPlugins(request):
-    """ return the path to the action plugin directory and a list of plugins there """
-    dir = os.path.join(request.cfg.plugin_dir, 'action')
-    plugins = []
-    if os.path.isdir(dir):
-        plugins = pysupport.getPackageModules(os.path.join(dir, 'dummy'))
-    return dir, plugins
+def getNames(cfg):
+    if hasattr(cfg, 'action_names'):
+        return cfg.action_names
+    else:
+        lnames = names[:]
+        lnames.extend(wikiutil.getPlugins('action', cfg))
+        cfg.action_names = lnames # remember it
+        return lnames
 
 def getHandler(request, action, identifier="execute"):
     """ return a handler function for a given action or None """
--- a/MoinMoin/action/info.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/action/info.py	Mon Aug 07 21:53:20 2006 +0200
@@ -13,6 +13,7 @@
 from MoinMoin import config, wikiutil, action
 from MoinMoin.Page import Page
 from MoinMoin.logfile import editlog
+from MoinMoin.widget import html
 
 def execute(pagename, request):
     """ show misc. infos about a page """
@@ -88,38 +89,41 @@
 
         may_revert = request.user.may.revert(pagename)
 
+        def render_action(text, query, **kw):
+            kw.update(rel='nofollow')
+            if 0: # diff button doesnt work XXX
+                params_html = []
+                for k, v in query.items():
+                    params_html.append('<input type="hidden" name="%s" value="%s">' % (k, v))
+                params_html = ''.join(params_html)
+                html = '''
+<form>
+<input type="submit" value="%s">
+%s
+</form>
+''' % (text, params_html)
+            else:
+                html = page.link_to(request, text, querystr=query, **kw)
+            return html
+
         # read in the complete log of this page
         log = editlog.EditLog(request, rootpagename=pagename)
         count = 0
         for line in log.reverse():
             rev = int(line.rev)
-            actions = ""
+            actions = []
             if line.action in ['SAVE', 'SAVENEW', 'SAVE/REVERT', ]:
                 size = page.size(rev=rev)
                 if count == 0: # latest page
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('view'),
-                        querystr=''))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('raw'),
-                        querystr='action=raw', rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('print'),
-                        querystr='action=print', rel='nofollow'))
+                    actions.append(render_action(_('view'), {'action': 'show'}))
+                    actions.append(render_action(_('raw'), {'action': 'raw'}))
+                    actions.append(render_action(_('print'), {'action': 'print'}))
                 else:
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('view'),
-                        querystr='action=recall&rev=%d' % rev, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('raw'),
-                        querystr='action=raw&rev=%d' % rev, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('print'),
-                        querystr='action=print&rev=%d' % rev, rel='nofollow'))
+                    actions.append(render_action(_('view'), {'action': 'recall', 'rev': '%d' % rev}))
+                    actions.append(render_action(_('raw'), {'action': 'raw', 'rev': '%d' % rev}))
+                    actions.append(render_action(_('print'), {'action': 'print', 'rev': '%d' % rev}))
                     if may_revert and size: # you can only revert to nonempty revisions
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('revert'),
-                            querystr='action=revert&rev=%d' % rev, rel='nofollow'))
+                        actions.append(render_action(_('revert'), {'action': 'revert', 'rev': '%d' % rev}))
                 if count == 0:
                     rchecked = ' checked="checked"'
                     lchecked = ''
@@ -149,20 +153,12 @@
                     except:
                         pass
                     if line.action == 'ATTNEW':
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('view'),
-                            querystr='action=AttachFile&do=view&target=%s' % filename, rel='nofollow'))
+                        actions.append(render_action(_('view'), {'action': 'AttachFile', 'do': 'view', 'target': '%s' % filename}))
                     elif line.action == 'ATTDRW':
-                        actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                            text=_('edit'),
-                            querystr='action=AttachFile&drawing=%s' % filename.replace(".draw", ""), rel='nofollow'))
+                        actions.append(render_action(_('edit'), {'action': 'AttachFile', 'drawing': '%s' % filename.replace(".draw", "")}))
 
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('get'),
-                        querystr='action=AttachFile&do=get&target=%s' % filename, rel='nofollow'))
-                    actions = '%s&nbsp;%s' % (actions, page.link_to(request,
-                        text=_('del'),
-                        querystr='action=AttachFile&do=del&target=%s' % filename, rel='nofollow'))
+                    actions.append(render_action(_('get'), {'action': 'AttachFile', 'do': 'get', 'target': '%s' % filename}))
+                    actions.append(render_action(_('del'), {'action': 'AttachFile', 'do': 'del', 'target': '%s' % filename}))
                     # XXX use?: wikiutil.escape(filename)
 
             history.addRow((
@@ -172,7 +168,7 @@
                 diff,
                 line.getEditor(request) or _("N/A"),
                 wikiutil.escape(comment) or '&nbsp;',
-                actions,
+                "&nbsp;".join(actions),
             ))
             count += 1
             if count >= 100:
@@ -181,24 +177,22 @@
         # print version history
         from MoinMoin.widget.browser import DataBrowserWidget
 
-        request.write('<h2>%s</h2>\n' % _('Revision History'))
+        request.write(unicode(html.H2().append(_('Revision History'))))
 
         if not count: # there was no entry in logfile
             request.write(_('No log entries found.'))
             return
 
-        # TODO: this form activates revert, which should use POST, but
-        # other actions should use get. Maybe we should put the revert
-        # into the page view itself, and not in this form.
-        request.write('<form method="GET" action="">\n')
-        request.write('<div id="page-history">\n')
-        request.write('<input type="hidden" name="action" value="diff">\n')
-
         history_table = DataBrowserWidget(request)
         history_table.setData(history)
-        history_table.render()
-        request.write('</div>\n')
-        request.write('</form>\n')
+
+        div = html.DIV(id="page-history")
+        div.append(html.INPUT(type="hidden", name="action", value="diff"))
+        div.append(history_table.toHTML())
+
+        form = html.FORM(method="GET", action="")
+        form.append(div)
+        request.write(unicode(form))
 
     # main function
     _ = request.getText
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/action/refresh.py	Mon Aug 07 21:53:20 2006 +0200
@@ -0,0 +1,24 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - refresh cache of a page
+
+    @copyright: 2000-2004 by Jürgen Hermann <jh@web.de>,
+                2006 by MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+from MoinMoin.Page import Page
+
+def execute(pagename, request):
+    """ Handle refresh action """
+    # Without arguments, refresh action will refresh the page text_html cache.
+    arena = request.form.get('arena', ['Page.py'])[0]
+    if arena == 'Page.py':
+        arena = Page(request, pagename)
+    key = request.form.get('key', ['text_html'])[0]
+
+    # Remove cache entry (if exists), and send the page
+    from MoinMoin import caching
+    caching.CacheEntry(request, arena, key, scope='item').remove()
+    caching.CacheEntry(request, arena, "pagelinks", scope='item').remove()
+    request.page.send_page(request)
+
--- a/MoinMoin/converter/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/converter/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -6,8 +6,3 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.util import pysupport
-
-# create a list of extension converters from the subpackage directory
-extension_converters = pysupport.getPackageModules(__file__)
-modules = extension_converters
--- a/MoinMoin/filter/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/filter/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -9,8 +9,7 @@
 import os
 from MoinMoin.util import pysupport
 
-filters = pysupport.getPackageModules(__file__)
-modules = filters
+modules = pysupport.getPackageModules(__file__)
 
 standard_codings = ['utf-8', 'iso-8859-15', 'iso-8859-1', ]
 
--- a/MoinMoin/i18n/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/i18n/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -61,10 +61,11 @@
         The very first time, this will be slow as it will load all languages,
         but next time it will be fast due to caching.
     """
+    request.clock.start('i18n_init')
     global languages
     if languages is None:
         meta_cache = caching.CacheEntry(request, 'i18n', 'meta', scope='farm')
-        i18n_dir = os.path.join(request.cfg.moinmoin_dir, 'i18n', 'mo')
+        i18n_dir = os.path.join(request.cfg.moinmoin_dir, 'i18n')
         if meta_cache.needsUpdate(i18n_dir):
             _languages = {}
             for lang_file in glob.glob(po_filename(request, language='*', domain='MoinMoin')): # only MoinMoin domain for now XXX
@@ -85,6 +86,7 @@
         _languages = pickle.loads(meta_cache.content())
         if languages is None:
             languages = _languages
+    request.clock.stop('i18n_init')
 
 
 class Translation(object):
@@ -162,6 +164,7 @@
         return text
 
     def loadLanguage(self, request):
+        request.clock.start('loadLanguage')
         cache = caching.CacheEntry(request, arena='i18n', key=self.language, scope='farm')
         langfilename = po_filename(request, self.language, self.domain)
         needsupdate = cache.needsUpdate(langfilename)
@@ -203,6 +206,7 @@
 
         self.formatted = uc_texts
         self.raw = uc_unformatted
+        request.clock.stop('loadLanguage')
 
 
 def getDirection(lang):
--- a/MoinMoin/logfile/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/logfile/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -6,10 +6,6 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.util import pysupport
-
-logfiles = pysupport.getPackageModules(__file__)
-
 import os, codecs, errno
 from MoinMoin import config, wikiutil
 
--- a/MoinMoin/macro/SystemInfo.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/macro/SystemInfo.py	Mon Aug 07 21:53:20 2006 +0200
@@ -97,15 +97,16 @@
     nonestr = _("NONE")
     row('Event log', _formatInReadableUnits(eventlogger.size()))
 
-    row(_('Global extension macros'), ', '.join(macro.extension_macros) or nonestr)
+    row(_('Global extension macros'), ', '.join(macro.modules) or nonestr)
     row(_('Local extension macros'),
         ', '.join(wikiutil.wikiPlugins('macro', Macro.cfg)) or nonestr)
 
-    ext_actions = [x for x in action.extension_actions
+    glob_actions = [x for x in action.modules
+                    if not x in request.cfg.actions_excluded]
+    row(_('Global extension actions'), ', '.join(glob_actions) or nonestr)
+    loc_actions = [x for x in wikiutil.wikiPlugins('action', Macro.cfg)
                    if not x in request.cfg.actions_excluded]
-    row(_('Global extension actions'), ', '.join(ext_actions) or nonestr)
-    row(_('Local extension actions'),
-        ', '.join(action.getPlugins(request)[1]) or nonestr)
+    row(_('Local extension actions'), ', '.join(loc_actions) or nonestr)
 
     row(_('Global parsers'), ', '.join(parser.modules) or nonestr)
     row(_('Local extension parsers'),
--- a/MoinMoin/macro/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/macro/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -17,9 +17,7 @@
 """
 
 from MoinMoin.util import pysupport
-
-extension_macros = pysupport.getPackageModules(__file__)
-modules = extension_macros
+modules = pysupport.getPackageModules(__file__)
 
 import re, time, os
 from MoinMoin import action, config, util
--- a/MoinMoin/parser/ParserBase.py	Mon Aug 07 21:52:40 2006 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-	MoinMoin - Base Source Parser
-
-    @copyright: 2002 by Taesu Pyo <bigflood@hitel.net>
-    @license: GNU GPL, see COPYING for details.
-
-    Docstrings and some refactoring by Oliver Graf <ograf@bitart.de>
-
-basic css:
-
-pre.codearea     { font-style: sans-serif; color: #000000; }
-
-pre.codearea span.ID       { color: #000000; }
-pre.codearea span.Char     { color: #004080; }
-pre.codearea span.Comment  { color: #808080; }
-pre.codearea span.Number   { color: #008080; font-weight: bold; }
-pre.codearea span.String   { color: #004080; }
-pre.codearea span.SPChar   { color: #0000C0; }
-pre.codearea span.ResWord  { color: #4040ff; font-weight: bold; }
-pre.codearea span.ConsWord { color: #008080; font-weight: bold; }
-
-"""
-
-import re, sys, sha
-from MoinMoin import config, wikiutil
-
-def parse_start_step(request, args):
-    """
-    Parses common Colorizer parameters start, step, numbers.
-    Uses L{wikiutil.parseAttributes} and sanitizes the results.
-
-    Start and step must be a non negative number and default to 1,
-    numbers might be on, off, or none and defaults to on. On or off
-    means that numbers are switchable via JavaScript (html formatter),
-    disabled means that numbers are disabled completely.
-
-    attrdict is returned as last element in the tuple, to enable the
-    calling parser to extract further arguments.
-
-    @param request: a request instance
-    @param args: the argument string
-
-    @returns: numbers, start, step, attrdict
-    """
-    nums, start, step = 1, 1, 1
-    attrs, msg = wikiutil.parseAttributes(request, args)
-    if not msg:
-        try:
-            start = int(attrs.get('start','"1"')[1:-1])
-        except ValueError:
-            pass
-        try:
-            step = int(attrs.get('step','"1"')[1:-1])
-        except ValueError:
-            pass
-        if attrs.get('numbers','"on"')[1:-1].lower() in ('off', 'false', 'no'):
-            nums = 0
-        elif attrs.get('numbers','"on"')[1:-1].lower() in ('none', 'disable'):
-            nums = -1
-    return nums, start, step, attrs
-
-class FormatTextBase:
-    pass
-
-class FormatText(FormatTextBase):
-    
-    def __init__(self, fmt):
-        self.fmt = fmt
-
-    def formatString(self, formatter, word):
-        return (formatter.code_token(1, self.fmt) +
-                formatter.text(word) +
-                formatter.code_token(0, self.fmt))
-
-class FormatTextID(FormatTextBase):
-    
-    def __init__(self, fmt, icase=0):
-        if not isinstance(fmt, FormatText):
-            self.def_fmt = FormatText(fmt)
-        else:
-            self.def_fmt = fmt
-        self._ignore_case = icase
-        self.fmt = {}
-
-    def addFormat(self, word, fmt):
-        if self._ignore_case:
-            word = word.lower()
-        self.fmt[word] = fmt
-        
-    def setDefaultFormat(self, fmt):
-        self.def_fmt = fmt
-        
-    def formatString(self, formatter, word):
-        if self._ignore_case:
-            sword = word.lower()
-        else:
-            sword = word
-        return self.fmt.get(sword,self.def_fmt).formatString(formatter, word)
-
-class FormattingRuleSingle:
-    
-    def __init__(self, name, str_re, icase=0):
-        self.name = name
-        self.str_re = str_re
-        
-    def getStartRe(self):
-        return self.str_re
-    
-    def getText(self, parser, hit):
-        return hit
-
-class FormattingRulePair:
-    
-    def __init__(self, name, str_begin, str_end, icase=0):
-        self.name = name
-        self.str_begin = str_begin
-        self.str_end = str_end
-        if icase:
-            self.end_re = re.compile(str_end, re.M|re.I)
-        else:
-            self.end_re = re.compile(str_end, re.M)
-        
-    def getStartRe(self):
-        return self.str_begin
-    
-    def getText(self, parser, hit):
-        match = self.end_re.search(parser.line, parser.lastpos)
-        if not match:
-            next_lastpos = len(parser.line)
-        else:
-            next_lastpos = match.end() + (match.end() == parser.lastpos)
-        r = parser.line[parser.lastpos:next_lastpos]
-        parser.lastpos = next_lastpos
-        return hit + r
-
-
-# ------------------------------------------------------------------------
-
-class ParserBase:
-
-    parsername = 'ParserBase'
-    
-    def __init__(self, raw, request, **kw):
-        self.raw = raw
-        self.request = request
-        self.show_nums, self.num_start, self.num_step, attrs = parse_start_step(request, kw.get('format_args',''))
-
-        self._ignore_case = 0
-        self._formatting_rules = []
-        self._formatting_rules_n2r = {}
-        self._formatting_rule_index = 0
-        self.rule_fmt = {}
-        self.line_count = len(raw.split('\n'))+1
-
-    def setupRules(self):
-        self.def_format = FormatText('Default')
-        self.ID_format = FormatTextID('ID', self._ignore_case)
-        self.addRuleFormat("ID",self.ID_format)
-        self.addRuleFormat("Operator")
-        self.addRuleFormat("Char")
-        self.addRuleFormat("Comment")
-        self.addRuleFormat("Number")
-        self.addRuleFormat("String")
-        self.addRuleFormat("SPChar")
-        self.addRuleFormat("ResWord")
-        self.addRuleFormat("ResWord2")
-        self.addRuleFormat("ConsWord")
-        self.addRuleFormat("Special")
-        self.addRuleFormat("Preprc")
-        self.addRuleFormat("Error")
-        self.reserved_word_format = FormatText('ResWord')
-        self.constant_word_format = FormatText('ConsWord')
-
-    def addRule(self, name, str_re):
-        self._formatting_rule_index += 1
-        n = "%s_%s" % (name, self._formatting_rule_index)
-        f = FormattingRuleSingle(name, str_re, self._ignore_case)
-        self._formatting_rules.append((n,f))
-        self._formatting_rules_n2r[n] = f
-
-    def addRulePair(self, name, start_re, end_re):
-        self._formatting_rule_index += 1
-        n = "%s_%s" % (name,self._formatting_rule_index)
-        f = FormattingRulePair(name, start_re, end_re, self._ignore_case)
-        self._formatting_rules.append((n,f))
-        self._formatting_rules_n2r[n] = f
-
-    def addWords(self, words, fmt):
-        if not isinstance(fmt,FormatTextBase):
-            fmt = FormatText(fmt)
-        for w in words:
-            self.ID_format.addFormat(w, fmt)
-
-    def addReserved(self, words):
-        self.addWords(words, self.reserved_word_format)
-
-    def addConstant(self, words):
-        self.addWords(words, self.constant_word_format)
-        
-    def addRuleFormat(self, name, fmt=None):
-        if fmt is None:
-            fmt = FormatText(name)
-        self.rule_fmt[name] = fmt
-
-    def format(self, formatter, form = None):
-        """ Send the text.
-        """
-
-        self.setupRules()
-
-        l = []
-        for n,f in self._formatting_rules:
-            l.append("(?P<%s>%s)" % (n,f.getStartRe()))
-        
-        if self._ignore_case:
-            scan_re = re.compile("|".join(l),re.M|re.I)
-        else:
-            scan_re = re.compile("|".join(l),re.M)
-
-        self.lastpos = 0
-        self.line = self.raw
-
-        self._code_id = sha.new(self.raw.encode(config.charset)).hexdigest()
-        self.request.write(formatter.code_area(1, self._code_id, self.parsername, self.show_nums, self.num_start, self.num_step))
-
-        self.request.write(formatter.code_line(1))
-            #formatter, len('%d' % (self.line_count,)))
-        
-        match = scan_re.search(self.line)
-
-        while match and self.lastpos < len(self.line):
-            # add the match we found
-            self.write_normal_text(formatter,
-                                   self.line[self.lastpos:match.start()])
-            self.lastpos = match.end() + (match.end() == self.lastpos)
-
-            self.write_match(formatter, match)
-
-            # search for the next one
-            match = scan_re.search(self.line, self.lastpos)
-
-        self.write_normal_text(formatter, self.line[self.lastpos:])
-
-        self.request.write(formatter.code_area(0, self._code_id))
-
-
-    def write_normal_text(self, formatter, text):
-        first = 1
-        for line in text.expandtabs(4).split('\n'):
-            if not first:
-                self.request.write(formatter.code_line(1))
-            else:
-                first = 0
-            self.request.write(formatter.text(line))
-
-    def write_match(self, formatter, match):
-        for n, hit in match.groupdict().items():
-            if not hit: continue
-            r = self._formatting_rules_n2r[n]
-            s = r.getText(self, hit)
-            c = self.rule_fmt.get(r.name,None)
-            if not c: c = self.def_format
-            first = 1
-            for line in s.expandtabs(4).split('\n'):
-                if not first:
-                    self.request.write(formatter.code_line(1))
-                else:
-                    first = 0
-                self.request.write(c.formatString(formatter, line))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/parser/_ParserBase.py	Mon Aug 07 21:53:20 2006 +0200
@@ -0,0 +1,270 @@
+# -*- coding: iso-8859-1 -*-
+"""
+	MoinMoin - Base Source Parser
+
+    @copyright: 2002 by Taesu Pyo <bigflood@hitel.net>
+    @license: GNU GPL, see COPYING for details.
+
+    Docstrings and some refactoring by Oliver Graf <ograf@bitart.de>
+
+basic css:
+
+pre.codearea     { font-style: sans-serif; color: #000000; }
+
+pre.codearea span.ID       { color: #000000; }
+pre.codearea span.Char     { color: #004080; }
+pre.codearea span.Comment  { color: #808080; }
+pre.codearea span.Number   { color: #008080; font-weight: bold; }
+pre.codearea span.String   { color: #004080; }
+pre.codearea span.SPChar   { color: #0000C0; }
+pre.codearea span.ResWord  { color: #4040ff; font-weight: bold; }
+pre.codearea span.ConsWord { color: #008080; font-weight: bold; }
+
+"""
+
+import re, sys, sha
+from MoinMoin import config, wikiutil
+
+def parse_start_step(request, args):
+    """
+    Parses common Colorizer parameters start, step, numbers.
+    Uses L{wikiutil.parseAttributes} and sanitizes the results.
+
+    Start and step must be a non negative number and default to 1,
+    numbers might be on, off, or none and defaults to on. On or off
+    means that numbers are switchable via JavaScript (html formatter),
+    disabled means that numbers are disabled completely.
+
+    attrdict is returned as last element in the tuple, to enable the
+    calling parser to extract further arguments.
+
+    @param request: a request instance
+    @param args: the argument string
+
+    @returns: numbers, start, step, attrdict
+    """
+    nums, start, step = 1, 1, 1
+    attrs, msg = wikiutil.parseAttributes(request, args)
+    if not msg:
+        try:
+            start = int(attrs.get('start','"1"')[1:-1])
+        except ValueError:
+            pass
+        try:
+            step = int(attrs.get('step','"1"')[1:-1])
+        except ValueError:
+            pass
+        if attrs.get('numbers','"on"')[1:-1].lower() in ('off', 'false', 'no'):
+            nums = 0
+        elif attrs.get('numbers','"on"')[1:-1].lower() in ('none', 'disable'):
+            nums = -1
+    return nums, start, step, attrs
+
+class FormatTextBase:
+    pass
+
+class FormatText(FormatTextBase):
+    
+    def __init__(self, fmt):
+        self.fmt = fmt
+
+    def formatString(self, formatter, word):
+        return (formatter.code_token(1, self.fmt) +
+                formatter.text(word) +
+                formatter.code_token(0, self.fmt))
+
+class FormatTextID(FormatTextBase):
+    
+    def __init__(self, fmt, icase=0):
+        if not isinstance(fmt, FormatText):
+            self.def_fmt = FormatText(fmt)
+        else:
+            self.def_fmt = fmt
+        self._ignore_case = icase
+        self.fmt = {}
+
+    def addFormat(self, word, fmt):
+        if self._ignore_case:
+            word = word.lower()
+        self.fmt[word] = fmt
+        
+    def setDefaultFormat(self, fmt):
+        self.def_fmt = fmt
+        
+    def formatString(self, formatter, word):
+        if self._ignore_case:
+            sword = word.lower()
+        else:
+            sword = word
+        return self.fmt.get(sword,self.def_fmt).formatString(formatter, word)
+
+class FormattingRuleSingle:
+    
+    def __init__(self, name, str_re, icase=0):
+        self.name = name
+        self.str_re = str_re
+        
+    def getStartRe(self):
+        return self.str_re
+    
+    def getText(self, parser, hit):
+        return hit
+
+class FormattingRulePair:
+    
+    def __init__(self, name, str_begin, str_end, icase=0):
+        self.name = name
+        self.str_begin = str_begin
+        self.str_end = str_end
+        if icase:
+            self.end_re = re.compile(str_end, re.M|re.I)
+        else:
+            self.end_re = re.compile(str_end, re.M)
+        
+    def getStartRe(self):
+        return self.str_begin
+    
+    def getText(self, parser, hit):
+        match = self.end_re.search(parser.line, parser.lastpos)
+        if not match:
+            next_lastpos = len(parser.line)
+        else:
+            next_lastpos = match.end() + (match.end() == parser.lastpos)
+        r = parser.line[parser.lastpos:next_lastpos]
+        parser.lastpos = next_lastpos
+        return hit + r
+
+
+# ------------------------------------------------------------------------
+
+class ParserBase:
+
+    parsername = 'ParserBase'
+    
+    def __init__(self, raw, request, **kw):
+        self.raw = raw
+        self.request = request
+        self.show_nums, self.num_start, self.num_step, attrs = parse_start_step(request, kw.get('format_args',''))
+
+        self._ignore_case = 0
+        self._formatting_rules = []
+        self._formatting_rules_n2r = {}
+        self._formatting_rule_index = 0
+        self.rule_fmt = {}
+        self.line_count = len(raw.split('\n'))+1
+
+    def setupRules(self):
+        self.def_format = FormatText('Default')
+        self.ID_format = FormatTextID('ID', self._ignore_case)
+        self.addRuleFormat("ID",self.ID_format)
+        self.addRuleFormat("Operator")
+        self.addRuleFormat("Char")
+        self.addRuleFormat("Comment")
+        self.addRuleFormat("Number")
+        self.addRuleFormat("String")
+        self.addRuleFormat("SPChar")
+        self.addRuleFormat("ResWord")
+        self.addRuleFormat("ResWord2")
+        self.addRuleFormat("ConsWord")
+        self.addRuleFormat("Special")
+        self.addRuleFormat("Preprc")
+        self.addRuleFormat("Error")
+        self.reserved_word_format = FormatText('ResWord')
+        self.constant_word_format = FormatText('ConsWord')
+
+    def addRule(self, name, str_re):
+        self._formatting_rule_index += 1
+        n = "%s_%s" % (name, self._formatting_rule_index)
+        f = FormattingRuleSingle(name, str_re, self._ignore_case)
+        self._formatting_rules.append((n,f))
+        self._formatting_rules_n2r[n] = f
+
+    def addRulePair(self, name, start_re, end_re):
+        self._formatting_rule_index += 1
+        n = "%s_%s" % (name,self._formatting_rule_index)
+        f = FormattingRulePair(name, start_re, end_re, self._ignore_case)
+        self._formatting_rules.append((n,f))
+        self._formatting_rules_n2r[n] = f
+
+    def addWords(self, words, fmt):
+        if not isinstance(fmt,FormatTextBase):
+            fmt = FormatText(fmt)
+        for w in words:
+            self.ID_format.addFormat(w, fmt)
+
+    def addReserved(self, words):
+        self.addWords(words, self.reserved_word_format)
+
+    def addConstant(self, words):
+        self.addWords(words, self.constant_word_format)
+        
+    def addRuleFormat(self, name, fmt=None):
+        if fmt is None:
+            fmt = FormatText(name)
+        self.rule_fmt[name] = fmt
+
+    def format(self, formatter, form = None):
+        """ Send the text.
+        """
+
+        self.setupRules()
+
+        l = []
+        for n,f in self._formatting_rules:
+            l.append("(?P<%s>%s)" % (n,f.getStartRe()))
+        
+        if self._ignore_case:
+            scan_re = re.compile("|".join(l),re.M|re.I)
+        else:
+            scan_re = re.compile("|".join(l),re.M)
+
+        self.lastpos = 0
+        self.line = self.raw
+
+        self._code_id = sha.new(self.raw.encode(config.charset)).hexdigest()
+        self.request.write(formatter.code_area(1, self._code_id, self.parsername, self.show_nums, self.num_start, self.num_step))
+
+        self.request.write(formatter.code_line(1))
+            #formatter, len('%d' % (self.line_count,)))
+        
+        match = scan_re.search(self.line)
+
+        while match and self.lastpos < len(self.line):
+            # add the match we found
+            self.write_normal_text(formatter,
+                                   self.line[self.lastpos:match.start()])
+            self.lastpos = match.end() + (match.end() == self.lastpos)
+
+            self.write_match(formatter, match)
+
+            # search for the next one
+            match = scan_re.search(self.line, self.lastpos)
+
+        self.write_normal_text(formatter, self.line[self.lastpos:])
+
+        self.request.write(formatter.code_area(0, self._code_id))
+
+
+    def write_normal_text(self, formatter, text):
+        first = 1
+        for line in text.expandtabs(4).split('\n'):
+            if not first:
+                self.request.write(formatter.code_line(1))
+            else:
+                first = 0
+            self.request.write(formatter.text(line))
+
+    def write_match(self, formatter, match):
+        for n, hit in match.groupdict().items():
+            if not hit: continue
+            r = self._formatting_rules_n2r[n]
+            s = r.getText(self, hit)
+            c = self.rule_fmt.get(r.name,None)
+            if not c: c = self.def_format
+            first = 1
+            for line in s.expandtabs(4).split('\n'):
+                if not first:
+                    self.request.write(formatter.code_line(1))
+                else:
+                    first = 0
+                self.request.write(c.formatString(formatter, line))
--- a/MoinMoin/parser/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/parser/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -9,5 +9,5 @@
     @license: GNU GPL, see COPYING for details.
 """
 from MoinMoin.util import pysupport
+modules = pysupport.getPackageModules(__file__)
 
-modules = pysupport.getPackageModules(__file__)
--- a/MoinMoin/parser/text_cplusplus.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/parser/text_cplusplus.py	Mon Aug 07 21:53:20 2006 +0200
@@ -23,7 +23,7 @@
 
 """
 
-from MoinMoin.parser.ParserBase import ParserBase
+from MoinMoin.parser._ParserBase import ParserBase
 
 Dependencies = []
 
--- a/MoinMoin/parser/text_diff.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/parser/text_diff.py	Mon Aug 07 21:53:20 2006 +0200
@@ -7,7 +7,7 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.parser.ParserBase import ParserBase
+from MoinMoin.parser._ParserBase import ParserBase
 
 class Parser(ParserBase):
     parsername = "ColorizedDiff"
--- a/MoinMoin/parser/text_java.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/parser/text_java.py	Mon Aug 07 21:53:20 2006 +0200
@@ -7,7 +7,7 @@
 
 """
 
-from MoinMoin.parser.ParserBase import ParserBase
+from MoinMoin.parser._ParserBase import ParserBase
 
 Dependencies = []
 
--- a/MoinMoin/parser/text_pascal.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/parser/text_pascal.py	Mon Aug 07 21:53:20 2006 +0200
@@ -6,7 +6,7 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.parser.ParserBase import ParserBase
+from MoinMoin.parser._ParserBase import ParserBase
 
 Dependencies = []
 
--- a/MoinMoin/parser/text_python.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/parser/text_python.py	Mon Aug 07 21:53:20 2006 +0200
@@ -9,7 +9,7 @@
 import StringIO
 import keyword, token, tokenize, sha
 from MoinMoin import config, wikiutil
-from MoinMoin.parser.ParserBase import parse_start_step
+from MoinMoin.parser._ParserBase import parse_start_step
 
 _KEYWORD = token.NT_OFFSET + 1
 _TEXT    = token.NT_OFFSET + 2
--- a/MoinMoin/request/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/request/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -31,21 +31,46 @@
     """
 
     def __init__(self):
-        self.timings = {'total': time.time()}
+        self.timings = {}
+        self.states = {}
 
     def start(self, timer):
-        self.timings[timer] = time.time() - self.timings.get(timer, 0)
+        state = self.states.setdefault(timer, 'new')
+        if state == 'new':
+            self.timings[timer] = time.time()
+            self.states[timer] = 'running'
+        elif state == 'running':
+            pass # this timer is already running, do nothing
+        elif state == 'stopped':
+            # if a timer is stopped, timings has the sum of all times it was running
+            self.timings[timer] = time.time() - self.timings[timer]
+            self.states[timer] = 'running'
 
     def stop(self, timer):
-        self.timings[timer] = time.time() - self.timings[timer]
+        state = self.states.setdefault(timer, 'neverstarted')
+        if state == 'running':
+            self.timings[timer] = time.time() - self.timings[timer]
+            self.states[timer] = 'stopped'
+        elif state == 'stopped':
+            pass # this timer already has been stopped, do nothing
+        elif state == 'neverstarted':
+            pass # this timer never has been started, do nothing
 
     def value(self, timer):
-        return "%.3f" % (self.timings[timer], )
+        state = self.states.setdefault(timer, 'nosuchtimer')
+        if state == 'stopped':
+            result = "%.3fs" % self.timings[timer]
+        elif state == 'running':
+            result = "%.3fs (still running)" % (time.time() - self.timings[timer])
+        else:
+            result = "- (%s)" % state
+        return result
 
     def dump(self):
         outlist = []
-        for timing in self.timings.items():
-            outlist.append("%s = %.3fs" % timing)
+        for timer in self.timings.keys():
+            value = self.value(timer)
+            outlist.append("%s = %s" % (timer, value))
         outlist.sort()
         return outlist
 
@@ -118,6 +143,8 @@
         else:
             self.writestack = []
             self.clock = Clock()
+            self.clock.start('total')
+            self.clock.start('base__init__')
             # order is important here!
             self.__dict__.update(properties)
             self._load_multi_cfg()
@@ -154,7 +181,7 @@
 
             rootname = u''
             self.rootpage = Page(self, rootname, is_rootpage=1)
-
+            
             from MoinMoin import i18n
             self.i18n = i18n
             i18n.i18n_init(self)
@@ -179,6 +206,7 @@
 
             self.opened_logs = 0
             self.reset()
+            self.clock.stop('base__init__')
 
     def surge_protect(self):
         """ check if someone requesting too much from us """
@@ -268,8 +296,10 @@
     def _load_multi_cfg(self):
         # protect against calling multiple times
         if not hasattr(self, 'cfg'):
+            self.clock.start('load_multi_cfg')
             from MoinMoin.config import multiconfig
             self.cfg = multiconfig.getConfig(self.url)
+            self.clock.stop('load_multi_cfg')
 
     def setAcceptedCharsets(self, accept_charset):
         """ Set accepted_charsets by parsing accept-charset header
@@ -633,21 +663,9 @@
             self.cfg._known_actions # check
         except AttributeError:
             from MoinMoin import action
-            # Add built in actions
-            actions = [name[3:] for name in action.__dict__ if name.startswith('do_')]
-
-            # Add plugins           
-            dummy, plugins = action.getPlugins(self)
-            actions.extend(plugins)
+            self.cfg._known_actions = set(action.getNames(self.cfg))
 
-            # Add extensions
-            actions.extend(action.extension_actions)
-
-            # TODO: Use set when we require Python 2.3
-            actions = dict(zip(actions, [''] * len(actions)))
-            self.cfg._known_actions = actions
-
-        # Return a copy, so clients will not change the dict.
+        # Return a copy, so clients will not change the set.
         return self.cfg._known_actions.copy()
 
     def getAvailableActions(self, page):
@@ -668,14 +686,10 @@
 
             # Filter non ui actions (starts with lower case letter)
             actions = self.getKnownActions()
-            for key in actions.keys():
-                if key[0].islower():
-                    del actions[key]
+            actions = [action for action in actions if not action[0].islower()]
 
             # Filter wiki excluded actions
-            for key in self.cfg.actions_excluded:
-                if key in actions:
-                    del actions[key]
+            actions = [action for action in actions if not action in self.cfg.actions_excluded]
 
             # Filter actions by page type, acl and user state
             excluded = []
@@ -685,11 +699,9 @@
                 # Prevent modification of underlay only pages, or pages
                 # the user can't write and can't delete
                 excluded = [u'RenamePage', u'DeletePage', ] # AttachFile must NOT be here!
-            for key in excluded:
-                if key in actions:
-                    del actions[key]
+            actions = [action for action in actions if not action in excluded]
 
-            self._available_actions = actions
+            self._available_actions = set(actions)
 
         # Return a copy, so clients will not change the dict.
         return self._available_actions.copy()
--- a/MoinMoin/script/__init__.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/script/__init__.py	Mon Aug 07 21:53:20 2006 +0200
@@ -7,12 +7,6 @@
     @license: GNU GPL, see COPYING for details.
 """
 
-from MoinMoin.util import pysupport
-
-# create a list of extension scripts from the subpackage directory
-extension_scripts = pysupport.getPackageModules(__file__)
-modules = extension_scripts
-
 import os, sys, time
 
 flag_quiet = 0
--- a/MoinMoin/widget/html.py	Mon Aug 07 21:52:40 2006 +0200
+++ b/MoinMoin/widget/html.py	Mon Aug 07 21:53:20 2006 +0200
@@ -272,6 +272,7 @@
 class DIV(CompositeElement):
     "generic language/style container"
     _ATTRS = {
+        'id': None,
         'class': None,
     }