Edgewall Software

Ticket #4356: trac_wiki_creole-r9337.patch

File trac_wiki_creole-r9337.patch, 13.5 KB (added by cboos, 2 years ago)

Patch updated to current trunk, improved [[...]] link support.

  • trac/wiki/formatter.py

    WikiFormatting: add some support for WikiCreole (#4356)
     - `**` token for bold and `//` token for italic. Note that they should be matched by themselves, i.e. one can't use `'''` to match `**` or `''` to match `//`
     - `[[target]]` or `[[target|label]]` TracLinks. If `target` has to contain a `|` character, it must be wrapped in a string quote, as usual with TracLinks. The content of `[[]]` can't contain the `]]` substring.
    
    Note that support for the [[http://meta.wikimedia.org/wiki/Help:Piped_link#Pipe_trick|pipe trick]] is simply "emulated" (and to some extent only), as the wiki text is not converted before save. It is consistent with the removal of the scheme we do anyway. The reverse pipe trick is not operational yet.
    
    diff --git a/trac/wiki/formatter.py b/trac/wiki/formatter.py
    a b class Formatter(object): 
    408408 
    409409    def _bold_formatter(self, match, fullmatch): 
    410410        return self.simple_tag_handler(match, '<strong>', '</strong>') 
     411    # should be <b> 
     412 
     413    def _bold_wc_formatter(self, match, fullmatch): 
     414        return self.simple_tag_handler(match, '<b>', '</b>') 
     415    # should be <strong> 
    411416 
    412417    def _italic_formatter(self, match, fullmatch): 
    413418        return self.simple_tag_handler(match, '<i>', '</i>') 
    414419 
     420    def _italic_wc_formatter(self, match, fullmatch): 
     421        return self.simple_tag_handler(match, '<em>', '</em>') 
     422 
    415423    def _underline_formatter(self, match, fullmatch): 
    416424        return self.simple_tag_handler(match, '<span class="underline">', 
    417425                                       '</span>') 
    class Formatter(object): 
    473481        ns = fullmatch.group('lns') 
    474482        target = self._unquote(fullmatch.group('ltgt')) 
    475483        label = fullmatch.group('label') 
     484        return self._make_lhref_link(match, fullmatch, rel, ns, target, label) 
     485 
     486    def _make_lhref_link(self, match, fullmatch, rel, ns, target, label): 
    476487        if not label: # e.g. `[http://target]` or `[wiki:target]` 
    477488            if target: 
    478489                if target.startswith('//'):     # for `[http://target]` 
    class Formatter(object): 
    512523                    query = '&' + query.lstrip('?') 
    513524            return tag.a(label, href=path + query + fragment) 
    514525        else: 
    515             return self._make_link(ns, target, match, label, fullmatch) 
     526            return self._make_link(ns or 'wiki', target or '', match, label, 
     527                                   fullmatch) 
    516528 
    517529    def _make_link(self, ns, target, match, label, fullmatch): 
    518530        # first check for an alias defined in trac.ini 
    class Formatter(object): 
    610622            label = format_to_oneliner(self.env, self.context, label) 
    611623        return '<span class="wikianchor" id="%s">%s</span>' % (anchor, label) 
    612624 
    613     # WikiMacros 
     625    # WikiMacros or WikiCreole links 
     626 
     627    def _macrolink_formatter(self, match, fullmatch): 
     628        # check for a known [[macro]] 
     629        macro_or_link = match[2:-2] 
     630        fullmatch = WikiParser._macro_re.match(macro_or_link) 
     631        if fullmatch: 
     632            name = fullmatch.group('macroname') 
     633            args = fullmatch.group('macroargs') 
     634            macro = False # not a macro 
     635            macrolist = name[-1] == '?' 
     636            if name.lower() == 'br' or name == '?': 
     637                macro = None 
     638            else: 
     639                macro = WikiProcessor(self, (name, name[:-1])[macrolist]) 
     640                if macro.error: 
     641                    macro = False 
     642            if macro is not False: 
     643                if macrolist: 
     644                    macro = WikiProcessor(self, 'MacroList') 
     645                return self._macro_formatter(match, fullmatch, macro) 
     646        fullmatch = WikiParser._creolelink_re.match(macro_or_link) 
     647        return self._lhref_formatter(match, fullmatch) 
    614648     
    615     def _macro_formatter(self, match, fullmatch): 
     649    def _macro_formatter(self, match, fullmatch, macro=None): 
    616650        name = fullmatch.group('macroname') 
    617651        if name.lower() == 'br': 
    618652            return '<br />' 
    619653        if name and name[-1] == '?': # Macro?() shortcut for MacroList(Macro) 
    620654            args = name[:-1] or '*' 
    621             name = 'MacroList' 
    622655        else: 
    623656            args = fullmatch.group('macroargs') 
    624657        try: 
    625             macro = WikiProcessor(self, name) 
    626658            return macro.process(args, in_paragraph=True) 
    627659        except Exception, e: 
    628660            self.env.log.error('Macro %s(%s) failed: %s' %  
    class OneLinerFormatter(Formatter): 
    12021234    def _table_row_sep_formatter(self, match, fullmatch): 
    12031235        return '' 
    12041236 
    1205     def _macro_formatter(self, match, fullmatch): 
     1237    def _macro_formatter(self, match, fullmatch, macro=None): 
    12061238        name = fullmatch.group('macroname') 
    12071239        if name.lower() == 'br': 
    12081240            return ' ' 
    class OutlineFormatter(Formatter): 
    12651297 
    12661298    # Avoid the possible side-effects of rendering WikiProcessors 
    12671299 
    1268     def _macro_formatter(self, match, fullmatch): 
     1300    def _macro_formatter(self, match, fullmatch, macro=None): 
    12691301        return '' 
    12701302 
    12711303    def handle_code_block(self, line, startmatch=None): 
  • trac/wiki/parser.py

    diff --git a/trac/wiki/parser.py b/trac/wiki/parser.py
    a b class WikiParser(Component): 
    3030 
    3131    BOLDITALIC_TOKEN = "'''''" 
    3232    BOLD_TOKEN = "'''" 
     33    BOLD_TOKEN_WIKICREOLE = r"\*\*" 
    3334    ITALIC_TOKEN = "''" 
     35    ITALIC_TOKEN_WIKICREOLE = "//" 
    3436    UNDERLINE_TOKEN = "__" 
    3537    STRIKE_TOKEN = "~~" 
    3638    SUBSCRIPT_TOKEN = ",," 
    class WikiParser(Component): 
    6365        # Font styles 
    6466        r"(?P<bolditalic>!?%s)" % BOLDITALIC_TOKEN, 
    6567        r"(?P<bold>!?%s)" % BOLD_TOKEN, 
     68        r"(?P<bold_wc>!?%s)" % BOLD_TOKEN_WIKICREOLE,         
    6669        r"(?P<italic>!?%s)" % ITALIC_TOKEN, 
     70        r"(?P<italic_wc>!?%s)" % ITALIC_TOKEN_WIKICREOLE,         
    6771        r"(?P<underline>!?%s)" % UNDERLINE_TOKEN, 
    6872        r"(?P<strike>!?%s)" % STRIKE_TOKEN, 
    6973        r"(?P<subscript>!?%s)" % SUBSCRIPT_TOKEN, 
    class WikiParser(Component): 
    7175        r"(?P<inlinecode>!?%s(?P<inline>.*?)%s)" \ 
    7276        % (STARTBLOCK_TOKEN, ENDBLOCK_TOKEN), 
    7377        r"(?P<inlinecode2>!?%s(?P<inline2>.*?)%s)" \ 
    74         % (INLINE_TOKEN, INLINE_TOKEN)] 
     78        % (INLINE_TOKEN, INLINE_TOKEN), 
     79        ] 
    7580 
    7681    # Rules provided by IWikiSyntaxProviders will be inserted here 
    7782 
    class WikiParser(Component): 
    96101        # [=#anchor] creation 
    97102        (r"(?P<anchor>!?\[=#(?P<anchorname>%s)" % XML_NAME + 
    98103         "(?P<anchorlabel>\s+[^\]]*)?\])"), 
    99         # [[macro]] call 
    100         (r"(?P<macro>!?\[\[(?P<macroname>[\w/+-]+\??|\?)" 
    101          r"(\]\]|\((?P<macroargs>.*?)\)\]\]))"), 
     104        # [[macro]] call or [[WikiCreole link]] 
     105        (r"(?P<macrolink>!?\[\[(?:[^]]|][^]])*\]\])"), 
    102106        # == heading == #hanchor 
    103107        r"(?P<heading>^\s*(?P<hdepth>={1,6})\s(?P<htext>.*?)" 
    104108        r"(?P<hanchor>#%s)?\s*$)" % XML_NAME, 
    class WikiParser(Component): 
    125129    _startblock_re = re.compile(r"\s*%s(?:%s|\s*$)" % 
    126130                                (STARTBLOCK, PROCESSOR)) 
    127131    _processor_param_re = re.compile(PROCESSOR_PARAM) 
    128     _anchor_re = re.compile('[^\w:.-]+', re.UNICODE) 
     132    _anchor_re = re.compile(r'[^\w:.-]+', re.UNICODE) 
     133 
     134    _macro_re = re.compile(r''' 
     135        (?P<macroname> [\w/+-]+ \?? | \? )     # macro, macro? or ? 
     136          (?: \( (?P<macroargs> .*? ) \) )? $  # optional arguments within () 
     137    ''', re.VERBOSE) 
     138 
     139    _creolelink_re = re.compile(r''' 
     140        (?: 
     141          (?P<rel> %(rel)s )                # rel is "./..." or "/..." 
     142        | (?: (?P<lns> %(scheme)s ) : )?    # lns is the optional "scheme:" 
     143            (?P<ltgt>                       # ltgt is the optional target 
     144              %(scheme)s : (?:%(quoted)s)   #   - "scheme:'...quoted..'" 
     145            | %(quoted)s                    #   - "'...quoted...'" 
     146            | [^|]+                         #   - anything but a '|' 
     147            )? 
     148        ) 
     149        \s* (?: \| (?P<label> .* ) )?       # optional label after a '|' 
     150 
     151        ''' % {'rel': LHREF_RELATIVE_TARGET, 
     152               'scheme': LINK_SCHEME, 
     153               'quoted': QUOTED_STRING}, re.VERBOSE) 
    129154 
    130155    def __init__(self): 
    131156        self._compiled_rules = None 
  • trac/wiki/tests/wiki-tests.txt

    diff --git a/trac/wiki/tests/wiki-tests.txt b/trac/wiki/tests/wiki-tests.txt
    a b This should be '''''bold and italic''''' 
    99This should be <strong><i>bold and italic</i></strong> 
    1010</p> 
    1111------------------------------ 
     12============================== Multiline bold italic markup (WikiCreole) 
     13**//bold 
     14italic (//not italic//) 
     15multiline//** 
     16------------------------------ 
     17<p> 
     18<b><em>bold 
     19italic (</em>not italic<em>) 
     20multiline</em></b> 
     21</p> 
     22------------------------------ 
    1223============================== Problematic markup: comma-separated list with a time + bold markup 
    132423:59,'''test''',123 
    1425------------------------------ 
    rfc-2396.compatible://link 
    433444<a class="ext-link" href="rfc-2396.compatible://link"><span class="icon"> </span>RFC 2396</a> 
    434445</p> 
    435446------------------------------ 
     447============================== WikiCreole style for the above examples 
     448[[link:WikiStart| Foo]] [[http://www.edgewall.com/|Edgewall]] 
     449 
     450[[link:Foo Bar|Foo Bar]] [[link:Foo Bar#baz|Foo Bar]] 
     451 
     452[[Foo Bar]] [[Foo Bar|Fu Bar]] [[Foo Bar#baz|Foo Bar]] 
     453 
     454[[link:Argv|"*argv[] versus **argv"]] 
     455 
     456[[link:test|"test.txt", line 123]] 
     457 
     458[[link:pl/de|%de]] 
     459 
     460i.e. [[mailto:cboos@neuf.fr|me]] 
     461 
     462[[th:]] 
     463[[th:|Trac Hacks]] 
     464 
     465[[svn+ssh://secureserver.org|SVN link]] 
     466[[rfc-2396.compatible://link|RFC 2396]] 
     467------------------------------ 
     468<p> 
     469<a class="text resolver" href="/stuff/WikiStart"> Foo</a> <a class="ext-link" href="http://www.edgewall.com/"><span class="icon"> </span>Edgewall</a> 
     470</p> 
     471<p> 
     472<a class="text resolver" href="/stuff/Foo%20Bar">Foo Bar</a> <a class="text resolver" href="/stuff/Foo%20Bar%23baz">Foo Bar</a> 
     473</p> 
     474<p> 
     475<a class="missing wiki" href="/wiki/Foo%20Bar" rel="nofollow">Foo Bar?</a> <a class="missing wiki" href="/wiki/Foo%20Bar" rel="nofollow">Fu Bar?</a> <a class="missing wiki" href="/wiki/Foo%20Bar#baz" rel="nofollow">Foo Bar?</a> 
     476</p> 
     477<p> 
     478<a class="text resolver" href="/stuff/Argv">*argv[] versus **argv</a> 
     479</p> 
     480<p> 
     481<a class="text resolver" href="/stuff/test">"test.txt", line 123</a> 
     482</p> 
     483<p> 
     484<a class="text resolver" href="/stuff/pl/de">%de</a> 
     485</p> 
     486<p> 
     487i.e. <a class="mail-link" href="mailto:cboos@neuf.fr"><span class="icon"> </span>me</a> 
     488</p> 
     489<p> 
     490<a class="ext-link" href="http://trac-hacks.org/intertrac/" title="Trac Hacks"><span class="icon"> </span>th</a> 
     491<a class="ext-link" href="http://trac-hacks.org/intertrac/" title="Trac Hacks"><span class="icon"> </span>Trac Hacks</a> 
     492</p> 
     493<p> 
     494<a class="ext-link" href="svn+ssh://secureserver.org"><span class="icon"> </span>SVN link</a> 
     495<a class="ext-link" href="rfc-2396.compatible://link"><span class="icon"> </span>RFC 2396</a> 
     496</p> 
     497------------------------------ 
     498============================== More WikiCreole examples  
     499[[coffeehouse setup|How to set up a coffee house]] 
     500 
     501[[link:Template]] 
     502[[link:Template|]] 
     503 
     504[[trac:wiki:Pipe (computing)|]] 
     505[[trac:wiki:Pipe (computing)|]] 
     506 
     507[[|b]] (might change to relative) 
     508------------------------------ 
     509<p> 
     510<a class="missing wiki" href="/wiki/coffeehouse%20setup" rel="nofollow">How to set up a coffee house?</a> 
     511</p> 
     512<p> 
     513<a class="text resolver" href="/stuff/Template">Template</a> 
     514<a class="text resolver" href="/stuff/Template">Template</a> 
     515</p> 
     516<p> 
     517<a class="ext-link" href="http://trac.edgewall.org/intertrac/wiki%3APipe%20%28computing%29" title="wiki:Pipe (computing) in Trac's Trac"><span class="icon"> </span>trac:wiki:Pipe (computing)</a> 
     518<a class="ext-link" href="http://trac.edgewall.org/intertrac/wiki%3APipe%20%28computing%29" title="wiki:Pipe (computing) in Trac's Trac"><span class="icon"> </span>trac:wiki:Pipe (computing)</a> 
     519</p> 
     520<p> 
     521<a class="wiki" href="/wiki/WikiStart">b</a> (might change to relative) 
     522</p> 
     523------------------------------ 
    436524============================== Link resolver counter examples 
    437525Test:[[BR]] There should be a 
    438526line break 
    Hello, Hello World, args = hej hopp 
    862950</p> 
    863951------------------------------ 
    864952Hello, [[HelloWorld(...)]] 
    865 ============================== Bad macro call 
     953============================== Bad macro call, but valid WikiCreole link 
    866954[[HelloWorld(hej hopp) ]] # This shouldnt executed as macro since it contain whitespace between ) and ] 
    867955------------------------------ 
    868956<p> 
    869 [[<a class="missing wiki" href="/wiki/HelloWorld" rel="nofollow">HelloWorld?</a>(hej hopp) ]] # This shouldnt executed as macro since it contain whitespace between ) and ] 
     957<a class="missing wiki" href="/wiki/HelloWorld%28hej%20hopp%29%20" rel="nofollow">HelloWorld(hej hopp) ?</a> # This shouldnt executed as macro since it contain whitespace between ) and ] 
    870958</p> 
    871959------------------------------ 
    872 [[<a class="missing wiki" href="/wiki/HelloWorld" rel="nofollow">HelloWorld?</a>(hej hopp) ]] # This shouldnt executed as macro since it contain whitespace between ) and ] 
     960<a class="missing wiki" href="/wiki/HelloWorld%28hej%20hopp%29%20" rel="nofollow">HelloWorld(hej hopp) ?</a> # This shouldnt executed as macro since it contain whitespace between ) and ] 
    873961============================== Another bad macro call 
    874962[[HelloWorld(hej hopp))]] # Extra right brace and still executed 
    875963------------------------------