Ticket #801: WikiProcessor_refactoring--cs1343.diff
| File WikiProcessor_refactoring--cs1343.diff, 20.6 kB (added by cboos@…, 3 years ago) |
|---|
-
trac/tests/wiki-tests.txt
216 216 <p> 217 217 <sup>superscript</sup>, <sub>subscript</sub>, normal. 218 218 </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 ============================== 244 Inline [[html(<B> Test </B>)]] text 245 ------------------------------ 246 <p> 247 Inline <B> Test </B> text 248 </p> -
trac/tests/wiki.py
4 4 import StringIO 5 5 import unittest 6 6 7 7 8 class WikiTestCase(unittest.TestCase): 8 9 9 10 def __init__(self, input, correct): … … 13 14 14 15 def test(self): 15 16 """Testing WikiFormatter""" 16 from trac import Href, Logging17 from trac import Href, Mimeview, Logging 17 18 class Environment: 18 19 def __init__(self): 19 20 self.log = Logging.logger_factory('null') … … 21 22 self.href = Href.Href('/') 22 23 self.abs_href = Href.Href('http://www.example.com/') 23 24 self._wiki_pages = {} 25 self.mimeview = Mimeview.Mimeview(self) 24 26 class Cursor: 25 27 def execute(self, *kwargs): pass 26 28 def fetchone(self): return [] … … 30 32 31 33 out = StringIO.StringIO() 32 34 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) 34 37 35 38 def suite(): 36 39 suite = unittest.TestSuite() -
trac/util.py
62 62 .replace('>', '>') \ 63 63 .replace('"', '"') 64 64 65 def unescape(text): 66 """Reverses Escapes &, <, > and \"""" 67 if not text: 68 return '' 69 if type(text) is StringType: 70 text = text.replace('"', '"') \ 71 .replace('>', '>') \ 72 .replace('<', '<') \ 73 .replace('&', '&') 74 return text 75 65 76 def get_first_line(text, maxlen): 66 77 """ 67 78 returns the first line of text. If the line is longer then -
trac/wikimacros/rst.py
44 44 raise EnvironmentError, 'Docutils version >= %s required, %s found' % (docutils_required, __version__) 45 45 46 46 from trac.Href import Href 47 from trac.WikiFormatter import WikiProcessor 47 48 48 49 __docformat__ = 'reStructuredText' 49 50 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/]+)+)')51 WIKI_LINK = re.compile(r'(?:wiki:)?(.+)') 52 #WIKI_LINK = re.compile(r'(?:wiki:)?(?P<wikilink>[A-Za-z][\w\-]*[^\w\#\?]*)') 52 53 TICKET_LINK = re.compile(r'(?:#(\d+))|(?:ticket:(\d+))') 53 54 REPORT_LINK = re.compile(r'(?:{(\d+)})|(?:report:(\d+))') 54 55 CHANGESET_LINK = re.compile(r'(?:\[(\d+)\])|(?:changeset:(\d+))') 55 56 FILE_LINK = re.compile(r'(?:browser|repos|source):([^#]+)#?(.*)') 56 57 58 #import trac.Logging 59 #log = trac.Logging.logger_factory(logtype='file', logfile="/tmp/debug_rst.txt", level='ALL') 60 57 61 def _wikipage(href, args): 58 62 return href.wiki(args[0]) 59 63 … … 72 76 return href.browser(path, rev) 73 77 74 78 # TracLink REs and callback functions 75 LINKS = [(WIKI_LINK, _wikipage), 76 (TICKET_LINK, _ticket), 79 LINKS = [(TICKET_LINK, _ticket), 77 80 (REPORT_LINK, _report), 78 81 (CHANGESET_LINK, _changeset), 79 (FILE_LINK, _browser)] 82 (FILE_LINK, _browser), 83 (WIKI_LINK, _wikipage)] 80 84 81 85 82 def trac_get_reference(env, rawtext, text):86 def trac_get_reference(env, rawtext, link, text): 83 87 for (pattern, function) in LINKS: 84 m = pattern.match( text)88 m = pattern.match(link) 85 89 if m: 86 90 g = filter(None, m.groups()) 87 91 missing = 0 92 if not text: 93 text = g[0] 88 94 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 + "?" 92 99 uri = function(env.href, g) 93 100 reference = nodes.reference(rawtext, text) 94 101 reference['refuri']= uri … … 120 127 121 128 .. _TracLink: http://projects.edgewall.com/trac/wiki/TracLinks 122 129 """ 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) 125 136 if reference: 126 137 return reference 127 138 # didn't find a match (invalid TracLink), … … 134 145 135 146 136 147 def 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) 138 155 if reference: 139 156 return [reference], [] 140 157 warning = nodes.warning(None, … … 160 177 rst.roles.register_local_role('trac', do_trac_role) 161 178 162 179 # 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 163 201 def code_block(name,arguments,options,content,lineno,content_offset,block_text,state,state_machine): 164 202 165 203 """Create a code-block directive for docutils. … … 167 205 Usage: .. code-block:: language 168 206 169 207 If the language can be syntax highlighted it will be.""" 170 171 172 173 from trac.WikiFormatter import Formatter174 175 208 language = arguments[0] 209 text = '\n'.join(content) 210 reference = code_formatter(language, text) 211 return [reference] 176 212 177 code_processor = None178 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 188 213 # These are documented at http://docutils.sourceforge.net/spec/howto/rst-directives.html. 189 214 code_block.arguments = ( 190 215 1, # Number of required arguments. … … 200 225 code_block.content = 1 # True if content is allowed. 201 226 # Register the directive with docutils. 202 227 rst.directives.register_directive('code-block',code_block) 228 rst.roles.register_local_role('code-block', code_role) 203 229 204 230 205 231 -
trac/Mimeview.py
52 52 'make':'text/x-makefile', 'mk':'text/x-makefile', 'Makefile':'text/x-makefile', 53 53 'mail':'text/x-mail', 54 54 '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', 56 56 'php':'text/x-php', 'php4':'text/x-php', 'php3':'text/x-php', 57 57 'ps':'application/postscript', 58 58 'psp':'text/x-psp', 59 'py':'text/x-python', 59 'py':'text/x-python', 'python':'text/x-python', 60 60 'pyx':'text/x-pyrex', 61 61 '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', 63 63 'rfc':'text/x-rfc', 64 64 'scm':'text/x-scheme', 65 65 'sh':'application/x-sh', … … 69 69 'tex':'text/x-tex', 70 70 'vba':'text/x-vba', 71 71 'bas':'text/x-vba', 72 'v':'text/x-verilog', 72 'v':'text/x-verilog', 'verilog':'text/x-verilog', 73 73 'vhd':'text/x-vhdl', 74 74 'vrml':'model/vrml', 75 75 'wrl':'model/vrml', -
trac/WikiFormatter.py
26 26 import StringIO 27 27 28 28 import util 29 import Mimeview 29 30 30 __all__ = ['Formatter', 'OneLinerFormatter', 'wiki_to_html', 'wiki_to_oneliner' ]31 __all__ = ['Formatter', 'OneLinerFormatter', 'wiki_to_html', 'wiki_to_oneliner', 'WikiProcessor'] 31 32 32 33 34 def 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 42 class 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 33 112 class CommonFormatter: 34 113 """This class contains the patterns common to both Formatter and 35 114 OneLinerFormatter""" … … 263 342 """ 264 343 _rules = [r"""(?P<svnimg>(source|repos):([^ ]+)\.(PNG|png|JPG|jpg|JPEG|jpeg|GIF|gif))"""] + \ 265 344 CommonFormatter._rules + \ 266 [r"""(?P<macro>!?\[\[(?P<macroname>[ a-zA-Z]+)(\((?P<macroargs>[^\)]*)\))?\]\])""",345 [r"""(?P<macro>!?\[\[(?P<macroname>[\w/+-]+)(\]\]|\((?P<macroargs>.*?)\)\]\]))""", 267 346 r"""(?P<heading>^\s*(?P<hdepth>=+)\s.*\s(?P=hdepth)\s*$)""", 268 347 r"""(?P<list>^(?P<ldepth>\s+)(?:\*|[0-9]+\.) )""", 269 348 r"""(?P<indent>^(?P<idepth>\s+)(?=\S))""", … … 273 352 r"""(?P<table_cell>\|\|)"""] 274 353 275 354 _compiled_rules = re.compile('(?:' + string.join(_rules, '|') + ')') 276 _processor_re = re.compile('#\!([ a-zA-Z0-9/+-]+)')355 _processor_re = re.compile('#\!([\w/+-]+)') 277 356 _anchor_re = re.compile('[^\w\d\.-:]+', re.UNICODE) 278 mime_type = ""357 #801- mime_type = "" 279 358 anchors = None 280 359 281 360 hdf = None … … 296 375 self.hdf = hdf 297 376 self.anchors = [] 298 377 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 err334 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 err342 return text343 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/ first363 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 macros367 macros = __import__('wikimacros.' + name, globals(), locals(), [])368 module = getattr(macros, name)369 return module.execute370 371 378 def _macro_formatter(self, match, fullmatch): 372 379 name = fullmatch.group('macroname') 373 380 if name in ['br', 'BR']: 374 381 return '<br />' 375 382 args = fullmatch.group('macroargs') 383 args = util.unescape(args) 376 384 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) 379 387 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) 382 389 383 390 def _heading_formatter(self, match, fullmatch): 384 391 match = match.strip() … … 533 540 else: 534 541 self.code_text += line + os.linesep 535 542 if not self.code_processor: 536 self.code_processor = Formatter.builtin_processors['default']543 self.code_processor = WikiProcessor(self.env, 'default') 537 544 elif line.strip() == '}}}': 538 545 self.in_code_block -= 1 539 546 if self.in_code_block == 0 and self.code_processor: 540 547 self.close_paragraph() 541 548 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)) 543 550 else: 544 551 self.code_text += line + os.linesep 545 552 elif not self.code_processor: 546 553 match = Formatter._processor_re.search(line) 547 554 if match: 548 555 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 563 560 else: 564 561 self.code_text += line + os.linesep 565 self.code_processor = Formatter.builtin_processors['default']562 self.code_processor = WikiProcessor(self.env, 'default') 566 563 else: 567 564 self.code_text += line + os.linesep 568 565
