Changeset 2921

Show
Ignore:
Timestamp:
12/19/07 08:07:33 (1 year ago)
Author:
athomas
Message:

Lots more 0.11 fixes thanks to osimons. Also made expression parser much more robust, adding support for normal Python "and", "or" and "not" operators and fixing the two-tag limit.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • tagsplugin/trunk/tractags/expr.py

    r2488 r2921  
    11import compiler 
    22import operator 
     3import parser 
    34import re 
    45from trac.core import TracError 
     
    1112class Expression: 
    1213    """ Pass a set of tags through a basic expression filter. 
    13      
    14         Supported operators are: unary - (not); binary +, - and | (and, and 
    15         not, or). All values in the expression are treated as tags. Any tag 
    16         not in the same form as a Python variable must be quoted. 
    17          
     14 
     15        Expressions are valid, basic Python expressions. 
     16 
    1817        eg. Match all objects tagged with ticket and workflow, and not tagged 
    1918        with wiki or closed. 
    20          
    21             (ticket+workflow)-(wiki|closed) 
     19 
     20            (ticket and workflow) and not (wiki or closed) 
    2221        """ 
    2322    __slots__ = ['ast', 'expression'] 
     
    2524 
    2625    def __init__(self, expression): 
    27         tokenizer = re.compile(r'''"[^"]*"|'[^']*'|[-|,+()\[\]]|[^-,|+()\[\]\s]+''') 
    28         expr = [] 
    29         for token in tokenizer.findall(expression): 
    30             if token not in '-|+()[],' and token[0] not in '"\'': 
    31                 expr.append('"%s"' % token) 
    32             else: 
    33                 expr.append(token) 
    34         self.expression = (u' '.join(expr)).encode('utf-8')  
     26        self.expression = expression.decode('utf-8') 
    3527        self.ast = compiler.parse(self.expression, 'eval') 
    3628 
     
    3830        """ Fetch tags used in this expression. """ 
    3931        tags = set() 
    40         import parser 
    4132        def walk(node): 
    4233            if node[0] == 1: 
     
    7465               and not self._visit(node.right, context) 
    7566 
    76     def _visit_add(self, node, context): 
    77         return self._visit(node.left, context) \ 
    78                and self._visit(node.right, context) 
     67    def _visit_not(self, node, context): 
     68        return not self._visit(node.expr, context) 
    7969 
    80     def _visit_bitor(self, node, context): 
    81         return self._visit(node.nodes[0], context) \ 
    82                or self._visit(node.nodes[1], context) 
     70    def _visit_and(self, node, context): 
     71        result = True 
     72        for arg in node.nodes: 
     73            result = result and self._visit(arg, context) 
     74            if not result: 
     75                return False 
     76        return True 
     77 
     78    _visit_add = _visit_and 
     79 
     80    def _visit_or(self, node, context): 
     81        result = False 
     82        for arg in node.nodes: 
     83            result = result or self._visit(arg, context) 
     84            if result: 
     85                return True 
     86        return False 
     87 
     88    _visit_bitor = _visit_or 
    8389 
    8490    def _visit_name(self, node, context): 
  • tagsplugin/trunk/tractags/htdocs/css/tractags.css

    r1831 r2921  
    3232        font-size: 10px; 
    3333        height: 2.2em; 
    34         margin: 0 0 2em 2em; 
    35         text-align: right; 
     34        margin: 0 0 15em 2em; 
     35        text-align: left; 
     36
     37#expression ul { 
     38    list-style: none; 
    3639} 
    3740#expression input { font-size: 10px } 
  • tagsplugin/trunk/tractags/macros.py

    r2569 r2921  
    3131 
    3232    def _current_page(self, req): 
    33         return req.hdf.getValue('wiki.page_name', '') 
     33        # FIXME Butt ugly - should be formatter.context.resource.id or something 
     34        return req.path_info.rstrip('/').split('/')[-1] 
    3435 
    3536    # IWikiMacroProvider methods 
     
    4445        return pydoc.getdoc(getattr(self, 'render_' + name.lower())) 
    4546 
    46     def render_macro(self, req, name, content): 
     47    def expand_macro(self, formatter, name, content): 
    4748        from trac.web.chrome import add_stylesheet 
    4849        from parseargs import parseargs 
    49         add_stylesheet(req, 'tags/css/tractags.css') 
     50        add_stylesheet(formatter.req, 'tags/css/tractags.css') 
    5051        # Translate macro args into python args 
    5152        args = [] 
     
    6162            raise TracError("Invalid arguments '%s' (%s %s)" % (content, e.__class__.__name__, e)) 
    6263 
    63         return getattr(self, 'render_' + name.lower(), content)(req, *args, **kwargs) 
     64        return getattr(self, 'render_' + name.lower(), content)(formatter.req, *args, **kwargs) 
    6465 
    6566    # Macro implementations 
     
    171172 
    172173        engine = TagEngine(self.env) 
    173         page_name = req.hdf.get('wiki.page_name'
     174        page_name = self._current_page(req
    174175        if page_name: 
    175176            tags = [tag == '.' and page_name or tag for tag in tags] 
     
    186187            try: 
    187188                expr = Expression(expression) 
     189                self.env.log.debug(repr(expr)) 
    188190            except Exception, e: 
    189191                self.env.log.error("Invalid expression '%s'" % expression, exc_info=True) 
  • tagsplugin/trunk/tractags/templates/tags.html

    r2918 r2921  
    1717          value="${tag_expression}"/> 
    1818        <input type="submit" value="Filter Tags" name="filter"/> 
     19        <div py:if="tag_expression_error" id="expression-error"> 
     20          <strong>Error:</strong> ${tag_expression_error} 
     21        </div> 
    1922        <div> 
    20           <strong>Note:</strong> See 
    21           <a href="http://trac-hacks.org/wiki/TagsPlugin">TracTags</a> for 
    22           information about using tag expressions. 
    23         </div> 
    24         <div py:if="tag_expression_error" id="expression-error"> 
    25           <strong>Error:</strong> ${expression_error} 
     23          <p><strong>Tag expressions:</strong></p> 
     24          <ul> 
     25            <li><strong>and</strong> for AND operation: tag1 and tag2</li> 
     26            <li><strong>or</strong> for OR operation: tag1 or tag2</li> 
     27            <li><strong>not</strong> for NOT operation: tag1 and not tag2</li> 
     28            <li><strong>()</strong> to group sub-expressions:(tag1 or tag2) and not tag3</li> 
     29            <li><strong>'quoted string'</strong> to quote words with non-ASCII characters</li> 
     30          </ul> 
    2631        </div> 
    2732      </form> 
  • tagsplugin/trunk/tractags/web_ui.py

    r2918 r2921  
    108108    implements(IRequestHandler, INavigationContributor, ITemplateProvider) 
    109109 
    110     def _prepare_wiki(self, req): 
    111         from tractags.api import TagEngine 
    112         page = req.path_info[6:] or 'WikiStart' 
    113         engine = TagEngine(self.env) 
    114         wikitags = engine.tagspace.wiki 
    115         tags = list(wikitags.get_tags(page)) 
    116         tags.sort() 
    117  
    118         action = req.args.get('action', 'view') 
    119         if action == 'edit': 
    120             req.hdf['tags'] = req.args.get('tags', ', '.join(tags)) 
    121         elif action == 'view': 
    122             hdf_tags = [] 
    123             for tag in tags: 
    124                 href, title = engine.get_tag_link(tag) 
    125                 hdf_tags.append({'name': tag, 
    126                                  'href': href, 
    127                                  'title': title}) 
    128             req.hdf['tags'] = hdf_tags 
     110    # This method is never really called by anything.... Remove? 
     111    # def _prepare_wiki(self, req): 
     112    #     from tractags.api import TagEngine 
     113    #     page = req.path_info[6:] or 'WikiStart' 
     114    #     engine = TagEngine(self.env) 
     115    #     wikitags = engine.tagspace.wiki 
     116    #     tags = list(wikitags.get_tags(page)) 
     117    #     tags.sort() 
     118    #  
     119    #     action = req.args.get('action', 'view') 
     120    #     if action == 'edit': 
     121    #         req.hdf['tags'] = req.args.get('tags', ', '.join(tags)) 
     122    #     elif action == 'view': 
     123    #         hdf_tags = [] 
     124    #         for tag in tags: 
     125    #             href, title = engine.get_tag_link(tag) 
     126    #             hdf_tags.append({'name': tag, 
     127    #                              'href': href, 
     128    #                              'title': title}) 
     129    #         req.hdf['tags'] = hdf_tags 
    129130 
    130131    # ITemplateProvider methods