Ticket #2258: search.py

File search.py, 4.6 kB (added by takanorig@gmail.com, 6 months ago)

patch for 0.11

Line 
1 from trac.core import *
2 from trac.search import ISearchSource, shorten_result
3 from trac.versioncontrol.api import Node
4 from trac.perm import IPermissionRequestor
5 from trac.util import Markup, escape
6 from trac.mimeview.api import Mimeview
7 import re
8 import posixpath
9 import os
10 from fnmatch import fnmatch
11
12 class TracRepoSearchPlugin(Component):
13     """ Search the source repository. """
14     implements(ISearchSource, IPermissionRequestor)
15
16     def _get_filters(self):
17         includes = [glob for glob in self.env.config.get('repo-search',
18                    'include', '').split(os.path.pathsep) if glob]
19         excludes = [glob for glob in self.env.config.get('repo-search',
20                    'exclude', '').split(os.path.pathsep) if glob]
21         return (includes, excludes)
22
23     def walk_repo(self, repo):
24         """ Walk all nodes in the repo that match the filters. """
25         includes, excludes = self._get_filters()
26
27         def searchable(path):
28             # Exclude paths
29             for exclude in excludes:
30                 if fnmatch(path, exclude):
31                     return 0
32
33             # Include paths
34             for include in includes:
35                 if fnmatch(path, include):
36                     return 1
37
38             return not includes
39
40         def do_walk(path):
41             node = repo.get_node(path)
42             basename = posixpath.basename(path)
43
44             if searchable(node.path):
45                 yield node
46
47             if node.kind == Node.DIRECTORY:
48                 for subnode in node.get_entries():
49                     for result in do_walk(subnode.path):
50                         yield result
51
52         for node in do_walk('/'):
53             yield node
54
55     # IPermissionRequestor methods
56     def get_permission_actions(self):
57         yield 'REPO_SEARCH'
58
59     # ISearchSource methods
60     def get_search_filters(self, req):
61         if req.perm.has_permission('REPO_SEARCH'):
62             yield ('repo', 'Source Repository', 0)
63
64     def get_search_results(self, req, query, filters):
65         if 'repo' not in filters:
66             return
67         repo = self.env.get_repository(req.authname)
68         if not isinstance(query, list):
69             query = query.split()
70         query = [q.lower() for q in query]
71         db = self.env.get_db_cnx()
72         include, excludes = self._get_filters()
73
74         to_unicode = Mimeview(self.env).to_unicode
75
76         # Use indexer if possible, otherwise fall back on brute force search.
77         try:
78             from tracreposearch.indexer import Indexer
79             self.indexer = Indexer(self.env)
80             self.indexer.reindex()
81             walker = lambda repo, query: [repo.get_node(filename) for filename
82                                           in self.indexer.find_words(query)]
83         except TracError, e:
84             self.env.log.warning(e)
85             self.env.log.warning('Falling back on full repository walk')
86             def full_walker(repo, query):
87                 for node in self.walk_repo(repo):
88                     # Search content
89                     matched = 1
90                     content = node.get_content()
91                     if not content:
92                         continue
93                     content = to_unicode(content.read().lower(), node.get_content_type())
94                     for term in query:
95                         if term not in content:
96                             matched = 0
97                             break
98                     if matched:
99                         yield node
100
101             walker = full_walker
102
103         if not req.perm.has_permission('REPO_SEARCH'):
104             return
105
106         def match_name(name):
107             for term in query:
108                 if term not in name:
109                     return 0
110             return 1
111
112         for node in walker(repo, query):
113             change = repo.get_changeset(node.rev)
114             if node.kind == Node.DIRECTORY:
115                 yield (self.env.href.browser(node.path),
116                        node.path, change.date, change.author,
117                        'Directory')
118             else:
119                 found = 0
120                 content = to_unicode(node.get_content().read(), node.get_content_type())
121                 for n, line in enumerate(content.splitlines()):
122                     line = line.lower()
123                     for q in query:
124                         idx = line.find(q)
125                         if idx != -1:
126                             found = n + 1
127                             break
128                     if found:
129                         break
130
131                 yield (self.env.href.browser(node.path) + (found and '#L%i' % found or ''),
132                        node.path, change.date, change.author,
133                        shorten_result(content, query))