Edgewall Software

Ticket #801: WikiProcessor_refactoring--cs1343.diff

File WikiProcessor_refactoring--cs1343.diff, 20.6 kB (added by cboos@…, 3 years ago)

Updated version of the patch

  • trac/tests/wiki-tests.txt

     
    216216<p> 
    217217<sup>superscript</sup>, <sub>subscript</sub>, normal. 
    218218</p> 
     219============================== 
     220[[HelloWorld(hej hopp)]] 
     221------------------------------ 
     222<p> 
     223<pre class="wiki">hej hopp</pre> 
     224</p> 
     225============================== 
     226[[HelloWorld(hej hopp) ]] # This shouldnt executed as macro since it contain whitespace between ) and ] 
     227------------------------------ 
     228<p> 
     229[[HelloWorld(hej hopp) ]] # This shouldnt executed as macro since it contain whitespace between ) and ] 
     230</p> 
     231============================== 
     232[[HelloWorld(hej hopp))]] # Extra right brace and still executed 
     233------------------------------ 
     234<p> 
     235<pre class="wiki">hej hopp)</pre> # Extra right brace and still executed 
     236</p> 
     237============================== 
     238[[HelloWorld(hej hopp)]] [[HelloWorld(hej hopp2)]] # Test non greedy match 
     239------------------------------ 
     240<p> 
     241<pre class="wiki">hej hopp</pre> <pre class="wiki">hej hopp2</pre> # Test non greedy match 
     242</p> 
     243============================== 
     244Inline [[html(<B> Test </B>)]] text 
     245------------------------------ 
     246<p> 
     247Inline <B> Test </B> text 
     248</p> 
  • trac/tests/wiki.py

     
    44import StringIO 
    55import unittest 
    66 
     7 
    78class WikiTestCase(unittest.TestCase): 
    89 
    910    def __init__(self, input, correct): 
     
    1314     
    1415    def test(self): 
    1516        """Testing WikiFormatter""" 
    16         from trac import Href, Logging 
     17        from trac import Href, Mimeview, Logging 
    1718        class Environment: 
    1819            def __init__(self): 
    1920                self.log = Logging.logger_factory('null') 
     
    2122                self.href = Href.Href('/') 
    2223                self.abs_href = Href.Href('http://www.example.com/') 
    2324                self._wiki_pages = {} 
     25                self.mimeview = Mimeview.Mimeview(self) 
    2426        class Cursor: 
    2527            def execute(self, *kwargs): pass 
    2628            def fetchone(self): return [] 
     
    3032 
    3133        out = StringIO.StringIO() 
    3234        Formatter(None, Environment(), Connection()).format(self.input, out) 
    33         self.assertEquals(self.correct, out.getvalue()) 
     35        v = out.getvalue().replace('\r','') 
     36        self.assertEquals(self.correct, v) 
    3437 
    3538def suite(): 
    3639    suite = unittest.TestSuite() 
  • trac/util.py

     
    6262                    .replace('>', '&gt;') \ 
    6363                    .replace('"', '&#34;') 
    6464 
     65def unescape(text): 
     66    """Reverses Escapes &, <, > and \"""" 
     67    if not text: 
     68        return '' 
     69    if type(text) is StringType: 
     70        text = text.replace('&#34;', '"') \ 
     71               .replace('&gt;', '>') \ 
     72               .replace('&lt;', '<') \ 
     73               .replace('&amp;', '&')  
     74    return text 
     75 
    6576def get_first_line(text, maxlen): 
    6677    """ 
    6778    returns the first line of text. If the line is longer then 
  • trac/wikimacros/rst.py

     
    4444    raise EnvironmentError, 'Docutils version >= %s required, %s found' % (docutils_required, __version__) 
    4545 
    4646from trac.Href import Href 
     47from trac.WikiFormatter import WikiProcessor 
    4748 
    4849__docformat__ = 'reStructuredText' 
    4950 
    50 WIKI_LINK = re.compile(r'(?:wiki:)?(?P<w>[A-Za-z][\w\#\?]*[^\w\#\?]*)') # Links must begin with Letters, \# ? so we can link inside pages. 
    51 #WIKI_LINK = re.compile(r'(?:wiki:)?(?P<w>(^|(?<=[^A-Za-z]))[!]?[A-Z][a-z/]+(?:[A-Z][a-z/]+)+)') 
     51WIKI_LINK = re.compile(r'(?:wiki:)?(.+)') 
     52#WIKI_LINK = re.compile(r'(?:wiki:)?(?P<wikilink>[A-Za-z][\w\-]*[^\w\#\?]*)') 
    5253TICKET_LINK = re.compile(r'(?:#(\d+))|(?:ticket:(\d+))') 
    5354REPORT_LINK = re.compile(r'(?:{(\d+)})|(?:report:(\d+))') 
    5455CHANGESET_LINK = re.compile(r'(?:\[(\d+)\])|(?:changeset:(\d+))') 
    5556FILE_LINK = re.compile(r'(?:browser|repos|source):([^#]+)#?(.*)') 
    5657 
     58#import trac.Logging 
     59#log = trac.Logging.logger_factory(logtype='file', logfile="/tmp/debug_rst.txt", level='ALL') 
     60 
    5761def _wikipage(href, args): 
    5862    return href.wiki(args[0]) 
    5963 
     
    7276    return href.browser(path, rev) 
    7377 
    7478# TracLink REs and callback functions 
    75 LINKS = [(WIKI_LINK, _wikipage), 
    76          (TICKET_LINK, _ticket), 
     79LINKS = [(TICKET_LINK, _ticket), 
    7780         (REPORT_LINK, _report), 
    7881         (CHANGESET_LINK, _changeset), 
    79          (FILE_LINK, _browser)] 
     82         (FILE_LINK, _browser), 
     83         (WIKI_LINK, _wikipage)] 
    8084 
    8185 
    82 def trac_get_reference(env, rawtext, text): 
     86def trac_get_reference(env, rawtext, link, text): 
    8387    for (pattern, function) in LINKS: 
    84         m = pattern.match(text) 
     88        m = pattern.match(link) 
    8589        if m: 
    8690            g = filter(None, m.groups()) 
    8791            missing = 0 
     92            if not text: 
     93                text = g[0] 
    8894            if pattern == WIKI_LINK: 
    89                 if not (env._wiki_pages.has_key(g[0])): 
    90                         missing = 1 
    91                         text = text + "?" 
     95                pagename = re.search(r'^[^\#]+',g[0]) 
     96                if not (env._wiki_pages.has_key(pagename.group())): 
     97                    missing = 1 
     98                    text = text + "?" 
    9299            uri = function(env.href, g) 
    93100            reference = nodes.reference(rawtext, text) 
    94101            reference['refuri']= uri 
     
    120127 
    121128    .. _TracLink: http://projects.edgewall.com/trac/wiki/TracLinks 
    122129    """ 
    123     text = arguments[int(len(arguments) == 2)] 
    124     reference = trac_get_reference(env, block_text, text) 
     130    link = arguments[0] 
     131    if len(arguments) == 2: 
     132        text = arguments[1] 
     133    else: 
     134        text = None 
     135    reference = trac_get_reference(env, block_text, link, text) 
    125136    if reference: 
    126137        return reference 
    127138    # didn't find a match (invalid TracLink), 
     
    134145 
    135146 
    136147def trac_role(env, name, rawtext, text, lineno, inliner, options={}, content=[]): 
    137     reference = trac_get_reference(env, rawtext, text) 
     148    args  = text.split(" ",1) 
     149    link = args[0] 
     150    if len(args)==2: 
     151        text = args[1] 
     152    else: 
     153        text = None 
     154    reference = trac_get_reference(env, rawtext, link, text) 
    138155    if reference: 
    139156        return [reference], [] 
    140157    warning = nodes.warning(None, 
     
    160177    rst.roles.register_local_role('trac', do_trac_role) 
    161178 
    162179    # The code_block could is taken from the leo plugin rst2 
     180    def code_formatter(language, text): 
     181        #log.debug("language '%s' args '%s'", % (language, arguments)) 
     182        Format = WikiProcessor(env, language) 
     183        html = Format.process(hdf, text)         
     184        #log.debug("language '%s' htmltext '%s'" % (language, html)) 
     185        raw = nodes.raw('',html, format='html') #(self, rawsource='', text='', *children, **attributes): 
     186        return raw 
     187         
     188    def code_role(name, rawtext, text, lineno, inliner, options={}, content=[]): 
     189        args  = text.split(":",1) 
     190        language = args[0] 
     191        if len(args)==2: 
     192            text = args[1] 
     193        else: 
     194            text = "" 
     195        #log.debug("coderole '%s' text '%s'" % (language, text)) 
     196        reference = code_formatter(language, text) 
     197        return [reference], [] 
     198         
     199 
     200       
    163201    def code_block(name,arguments,options,content,lineno,content_offset,block_text,state,state_machine): 
    164202 
    165203        """Create a code-block directive for docutils. 
     
    167205        Usage: .. code-block:: language 
    168206 
    169207        If the language can be syntax highlighted it will be.""" 
    170  
    171  
    172          
    173         from trac.WikiFormatter import Formatter 
    174          
    175208        language = arguments[0] 
     209        text = '\n'.join(content)         
     210        reference = code_formatter(language, text) 
     211        return [reference] 
    176212 
    177         code_processor = None 
    178         if  Formatter.builtin_processors.has_key(language): 
    179             code_processor = Formatter.builtin_processors[language] 
    180         else: 
    181             code_processor = Formatter.builtin_processors['default'] 
    182  
    183  
    184         html = code_processor(hdf, '\n'.join(content), env)         
    185         raw = nodes.raw('',html, format='html') #(self, rawsource='', text='', *children, **attributes): 
    186         return [raw] 
    187  
    188213    # These are documented at http://docutils.sourceforge.net/spec/howto/rst-directives.html. 
    189214    code_block.arguments = ( 
    190215        1, # Number of required arguments. 
     
    200225    code_block.content = 1 # True if content is allowed. 
    201226    # Register the directive with docutils. 
    202227    rst.directives.register_directive('code-block',code_block) 
     228    rst.roles.register_local_role('code-block', code_role) 
    203229     
    204230     
    205231 
  • trac/Mimeview.py

     
    5252    'make':'text/x-makefile', 'mk':'text/x-makefile', 'Makefile':'text/x-makefile', 
    5353    'mail':'text/x-mail', 
    5454    'pas':'text/x-pascal', 
    55     'pl':'text/x-perl', 'pm':'text/x-perl', 'PL':'text/x-perl', 
     55    'pl':'text/x-perl', 'pm':'text/x-perl', 'PL':'text/x-perl', 'perl':'text/x-perl', 
    5656    'php':'text/x-php', 'php4':'text/x-php', 'php3':'text/x-php', 
    5757    'ps':'application/postscript', 
    5858    'psp':'text/x-psp', 
    59     'py':'text/x-python', 
     59    'py':'text/x-python', 'python':'text/x-python', 
    6060    'pyx':'text/x-pyrex', 
    6161    'nroff':'application/x-troff', 'roff':'application/x-troff', 'troff':'application/x-troff', 
    62     'rb':'text/x-ruby', 
     62    'rb':'text/x-ruby', 'ruby':'text/x-ruby', 
    6363    'rfc':'text/x-rfc', 
    6464    'scm':'text/x-scheme', 
    6565    'sh':'application/x-sh', 
     
    6969    'tex':'text/x-tex', 
    7070    'vba':'text/x-vba', 
    7171    'bas':'text/x-vba', 
    72     'v':'text/x-verilog', 
     72    'v':'text/x-verilog', 'verilog':'text/x-verilog', 
    7373    'vhd':'text/x-vhdl', 
    7474    'vrml':'model/vrml', 
    7575    'wrl':'model/vrml', 
  • trac/WikiFormatter.py

     
    2626import StringIO 
    2727 
    2828import util 
     29import Mimeview 
    2930 
    30 __all__ = ['Formatter', 'OneLinerFormatter', 'wiki_to_html', 'wiki_to_oneliner'] 
     31__all__ = ['Formatter', 'OneLinerFormatter', 'wiki_to_html', 'wiki_to_oneliner', 'WikiProcessor'] 
    3132 
    3233 
     34def system_message(msg, text): 
     35    return """<div class="system-message"> 
     36 <strong>%s</strong> 
     37 <pre>%s</pre> 
     38</div> 
     39""" % (msg, util.escape(text)) 
     40     
     41 
     42class WikiProcessor: 
     43 
     44    mime_type = "" 
     45 
     46    def __init__(self, env, name): 
     47        self.env = env 
     48        self.error = self.set_code_processor(name) 
     49     
     50    def default_processor(hdf, text, env): 
     51        return '<pre class="wiki">' + util.escape(text) + '</pre>' 
     52     
     53    def html_processor(hdf, text, env): 
     54        if Formatter._htmlproc_disallow_rule.search(text): 
     55            err = system_message('Error: HTML block contains disallowed tags.', text) 
     56            env.log.error(err) 
     57            return err 
     58        if Formatter._htmlproc_disallow_attribute.search(text): 
     59            err = system_message('Error: HTML block contains disallowed attributes.', text) 
     60            env.log.error(err) 
     61            return err 
     62        return text 
     63 
     64    def mime_processor(self, hdf, text, env): 
     65        return env.mimeview.display(text, self.mime_type) 
     66     
     67    builtin_processors = { 'html': html_processor, 
     68                           'default': default_processor} 
     69 
     70    def process(self, hdf, text, inline=False): 
     71        text = self.code_processor(hdf, text, self.env) 
     72        if inline: 
     73            code_block_start = re.compile('^<div class="code-block">') 
     74            code_block_end = re.compile('</div>$') 
     75            text, nr = code_block_start.subn('<span class="code-block">', text, 1 ) 
     76            if nr: 
     77                text, nr = code_block_end.subn('</span>', text, 1 ) 
     78            return text 
     79        else: 
     80            return text 
     81     
     82 
     83    def set_code_processor(self, name): 
     84        if  self.builtin_processors.has_key(name): 
     85            self.code_processor = self.builtin_processors[name] 
     86        else: 
     87            try: 
     88                self.code_processor = self.load_macro(name) 
     89            except Exception, e: 
     90                if Mimeview.MIME_MAP.has_key(name): 
     91                    name = Mimeview.MIME_MAP[name] 
     92                mimeviewer, exists = self.env.mimeview.get_viewer(name) 
     93                if exists != -1: 
     94                    self.mime_type = name 
     95                    self.code_processor = self.mime_processor 
     96                else: 
     97                    self.code_processor = self.builtin_processors['default'] 
     98                    return 1 
     99        return 0 
     100     
     101    def load_macro(self, name): 
     102        # Look in envdir/wiki-macros/ first 
     103        try: 
     104            module = imp.load_source(name, os.path.join(self.env.path, 'wiki-macros', name+'.py')) 
     105        except IOError: 
     106            # fall back to site-wide macros 
     107            macros = __import__('wikimacros.' + name, globals(),  locals(), []) 
     108            module = getattr(macros, name) 
     109        return module.execute 
     110 
     111 
    33112class CommonFormatter: 
    34113    """This class contains the patterns common to both Formatter and 
    35114    OneLinerFormatter""" 
     
    263342    """ 
    264343    _rules = [r"""(?P<svnimg>(source|repos):([^ ]+)\.(PNG|png|JPG|jpg|JPEG|jpeg|GIF|gif))"""] + \ 
    265344             CommonFormatter._rules + \ 
    266              [r"""(?P<macro>!?\[\[(?P<macroname>[a-zA-Z]+)(\((?P<macroargs>[^\)]*)\))?\]\])""", 
     345             [r"""(?P<macro>!?\[\[(?P<macroname>[\w/+-]+)(\]\]|\((?P<macroargs>.*?)\)\]\]))""", 
    267346              r"""(?P<heading>^\s*(?P<hdepth>=+)\s.*\s(?P=hdepth)\s*$)""", 
    268347              r"""(?P<list>^(?P<ldepth>\s+)(?:\*|[0-9]+\.) )""", 
    269348              r"""(?P<indent>^(?P<idepth>\s+)(?=\S))""", 
     
    273352              r"""(?P<table_cell>\|\|)"""] 
    274353 
    275354    _compiled_rules = re.compile('(?:' + string.join(_rules, '|') + ')') 
    276     _processor_re = re.compile('#\!([a-zA-Z0-9/+-]+)') 
     355    _processor_re = re.compile('#\!([\w/+-]+)') 
    277356    _anchor_re = re.compile('[^\w\d\.-:]+', re.UNICODE) 
    278     mime_type = "" 
     357    #801- mime_type = "" 
    279358    anchors = None 
    280359 
    281360    hdf = None 
     
    296375        self.hdf = hdf 
    297376        self.anchors = [] 
    298377 
    299     def default_processor(hdf, text, env): 
    300         return '<pre class="wiki">' + util.escape(text) + '</pre>' 
    301     def asp_processor(hdf, text, env): 
    302         return env.mimeview.display(text, 'text/x-asp') 
    303     def c_processor(hdf, text, env): 
    304         return env.mimeview.display(text, 'text/x-csrc') 
    305     def css_processor(hdf, text, env): 
    306         return env.mimeview.display(text, 'text/css') 
    307     def java_processor(hdf, text, env): 
    308         return env.mimeview.display(text, 'text/x-java') 
    309     def cpp_processor(hdf, text, env): 
    310         return env.mimeview.display(text, 'text/x-c++src') 
    311     def perl_processor(hdf, text, env): 
    312         return env.mimeview.display(text, 'text/x-perl') 
    313     def php_processor(hdf, text, env): 
    314         return env.mimeview.display(text, 'text/x-php') 
    315     def python_processor(hdf, text, env): 
    316         return env.mimeview.display(text, 'text/x-python') 
    317     def ruby_processor(hdf, text, env): 
    318         return env.mimeview.display(text, 'text/x-ruby') 
    319     def sql_processor(hdf, text, env): 
    320         return env.mimeview.display(text, 'text/x-sql') 
    321     def xml_processor(hdf, text, env): 
    322         return env.mimeview.display(text, 'text/xml') 
    323     def verilog_processor(hdf, text, env): 
    324         return env.mimeview.display(text, 'text/x-verilog') 
    325     def html_processor(hdf, text, env): 
    326         if Formatter._htmlproc_disallow_rule.search(text): 
    327             err = """\ 
    328 <div class="system-message"> 
    329  <strong>Error: HTML block contains disallowed tags.</strong> 
    330  <pre>%s</pre> 
    331 </div>\n""" % util.escape(text) 
    332             env.log.error(err) 
    333             return err 
    334         if Formatter._htmlproc_disallow_attribute.search(text): 
    335             err = """\ 
    336 <div class="system-message"> 
    337  <strong>Error: HTML block contains disallowed attributes.</strong> 
    338  <pre>%s</pre> 
    339 </div>\n""" % util.escape(text) 
    340             env.log.error(err) 
    341             return err 
    342         return text 
    343     def mime_processor(self, hdf, text, env): 
    344         return env.mimeview.display(text, self.mime_type) 
    345  
    346     builtin_processors = { 'html': html_processor, 
    347                            'asp': asp_processor, 
    348                            'c': c_processor, 
    349                            'css': css_processor, 
    350                            'cpp': cpp_processor, 
    351                            'java': java_processor, 
    352                            'php': php_processor, 
    353                            'perl': perl_processor, 
    354                            'python': python_processor, 
    355                            'ruby': ruby_processor, 
    356                            'sql': sql_processor, 
    357                            'xml': xml_processor, 
    358                            'verilog': verilog_processor, 
    359                            'default': default_processor} 
    360  
    361     def load_macro(self, name): 
    362         # Look in envdir/wiki-macros/ first 
    363         try: 
    364             module = imp.load_source(name, os.path.join(self.env.path, 'wiki-macros', name+'.py')) 
    365         except IOError: 
    366             # fall back to site-wide macros 
    367             macros = __import__('wikimacros.' + name, globals(),  locals(), []) 
    368             module = getattr(macros, name) 
    369         return module.execute 
    370  
    371378    def _macro_formatter(self, match, fullmatch): 
    372379        name = fullmatch.group('macroname') 
    373380        if name in ['br', 'BR']: 
    374381            return '<br />' 
    375382        args = fullmatch.group('macroargs') 
     383        args = util.unescape(args) 
    376384        try: 
    377             macro = self.load_macro(name) 
    378             return macro(self.hdf, args, self.env) 
     385            macro = WikiProcessor(self.env, name) 
     386            return macro.process(self.hdf, args, 1) 
    379387        except Exception, e: 
    380             return '<div class="system-message"><strong>Error: Macro %s(%s) failed</strong><pre>%s</pre></div>' \ 
    381                    % (name, args, e) 
     388            return system_message('Error: Macro %s(%s) failed' % (name, args), e) 
    382389 
    383390    def _heading_formatter(self, match, fullmatch): 
    384391        match = match.strip() 
     
    533540            else: 
    534541                self.code_text += line + os.linesep 
    535542                if not self.code_processor: 
    536                     self.code_processor = Formatter.builtin_processors['default'] 
     543                    self.code_processor = WikiProcessor(self.env, 'default') 
    537544        elif line.strip() == '}}}': 
    538545            self.in_code_block -= 1 
    539546            if self.in_code_block == 0 and self.code_processor: 
    540547                self.close_paragraph() 
    541548                self.close_table() 
    542                 self.out.write(self.code_processor(self.hdf, self.code_text, self.env)) 
     549                self.out.write(self.code_processor.process(self.hdf, self.code_text)) 
    543550            else: 
    544551                self.code_text += line + os.linesep 
    545552        elif not self.code_processor: 
    546553            match = Formatter._processor_re.search(line) 
    547554            if match: 
    548555                name = match.group(1) 
    549                 if  Formatter.builtin_processors.has_key(name): 
    550                     self.code_processor = Formatter.builtin_processors[name] 
    551                 else: 
    552                     try: 
    553                         self.code_processor = self.load_macro(name) 
    554                     except Exception, e: 
    555                         mimeviewer, exists = self.env.mimeview.get_viewer(name) 
    556                         if exists != -1: 
    557                             self.mime_type = name 
    558                             self.code_processor = self.mime_processor 
    559                         else: 
    560                             self.code_text += line + os.linesep 
    561                             self.code_processor = Formatter.builtin_processors['default'] 
    562                             self.out.write('<div class="system-message"><strong>Error: Failed to load processor <code>%s</code></strong>:<pre>%s</pre></div>' % (name, e)) 
     556                self.code_processor = WikiProcessor(self.env, name) 
     557                if self.code_processor.error: 
     558                    self.out.write(system_message('Error: Failed to load processor <code>%s</code>' % name, e)) 
     559                    self.code_text += line + os.linesep 
    563560            else: 
    564561                self.code_text += line + os.linesep  
    565                 self.code_processor = Formatter.builtin_processors['default'] 
     562                self.code_processor = WikiProcessor(self.env, 'default') 
    566563        else: 
    567564            self.code_text += line + os.linesep 
    568565