##// END OF EJS Templates
bumbed whoosh to 2.3.X series...
marcink -
r1995:b6c902d8 beta
parent child Browse files
Show More
@@ -1,18 +1,18 b''
1 Pylons==1.0.0
1 Pylons==1.0.0
2 Beaker==1.6.2
2 Beaker==1.6.2
3 WebHelpers>=1.2
3 WebHelpers>=1.2
4 formencode==1.2.4
4 formencode==1.2.4
5 SQLAlchemy==0.7.4
5 SQLAlchemy==0.7.4
6 Mako==0.5.0
6 Mako==0.5.0
7 pygments>=1.4
7 pygments>=1.4
8 whoosh<1.8
8 whoosh<2.4
9 celery>=2.2.5,<2.3
9 celery>=2.2.5,<2.3
10 babel
10 babel
11 python-dateutil>=1.5.0,<2.0.0
11 python-dateutil>=1.5.0,<2.0.0
12 dulwich>=0.8.0,<0.9.0
12 dulwich>=0.8.0,<0.9.0
13 vcs>=0.2.3.dev
13 vcs>=0.2.3.dev
14 webob==1.0.8
14 webob==1.0.8
15 markdown==2.0.3
15 markdown==2.0.3
16 docutils==0.8.1
16 docutils==0.8.1
17 py-bcrypt
17 py-bcrypt
18 mercurial>=2.1,<2.2 No newline at end of file
18 mercurial>=2.1,<2.2
@@ -1,93 +1,93 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.__init__
3 rhodecode.__init__
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode, a web based repository management based on pylons
6 RhodeCode, a web based repository management based on pylons
7 versioning implementation: http://semver.org/
7 versioning implementation: http://semver.org/
8
8
9 :created_on: Apr 9, 2010
9 :created_on: Apr 9, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import sys
26 import sys
27 import platform
27 import platform
28
28
29 VERSION = (1, 3, 0, 'beta')
29 VERSION = (1, 3, 0, 'beta')
30 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 __version__ = '.'.join((str(each) for each in VERSION[:4]))
31 __dbversion__ = 4 # defines current db version for migrations
31 __dbversion__ = 4 # defines current db version for migrations
32 __platform__ = platform.system()
32 __platform__ = platform.system()
33 __license__ = 'GPLv3'
33 __license__ = 'GPLv3'
34 __py_version__ = sys.version_info
34 __py_version__ = sys.version_info
35
35
36 PLATFORM_WIN = ('Windows')
36 PLATFORM_WIN = ('Windows')
37 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
37 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
38
38
39 requirements = [
39 requirements = [
40 "Pylons==1.0.0",
40 "Pylons==1.0.0",
41 "Beaker==1.6.2",
41 "Beaker==1.6.2",
42 "WebHelpers>=1.2",
42 "WebHelpers>=1.2",
43 "formencode==1.2.4",
43 "formencode==1.2.4",
44 "SQLAlchemy==0.7.4",
44 "SQLAlchemy==0.7.4",
45 "Mako==0.5.0",
45 "Mako==0.5.0",
46 "pygments>=1.4",
46 "pygments>=1.4",
47 "whoosh<1.8",
47 "whoosh<2.4",
48 "celery>=2.2.5,<2.3",
48 "celery>=2.2.5,<2.3",
49 "babel",
49 "babel",
50 "python-dateutil>=1.5.0,<2.0.0",
50 "python-dateutil>=1.5.0,<2.0.0",
51 "dulwich>=0.8.0,<0.9.0",
51 "dulwich>=0.8.0,<0.9.0",
52 "vcs>=0.2.3.dev",
52 "vcs>=0.2.3.dev",
53 "webob==1.0.8",
53 "webob==1.0.8",
54 "markdown==2.0.3",
54 "markdown==2.0.3",
55 "docutils==0.8.1",
55 "docutils==0.8.1",
56 ]
56 ]
57
57
58 if __py_version__ < (2, 6):
58 if __py_version__ < (2, 6):
59 requirements.append("simplejson")
59 requirements.append("simplejson")
60 requirements.append("pysqlite")
60 requirements.append("pysqlite")
61
61
62 if __platform__ in PLATFORM_WIN:
62 if __platform__ in PLATFORM_WIN:
63 requirements.append("mercurial>=2.1,<2.2")
63 requirements.append("mercurial>=2.1,<2.2")
64 else:
64 else:
65 requirements.append("py-bcrypt")
65 requirements.append("py-bcrypt")
66 requirements.append("mercurial>=2.1,<2.2")
66 requirements.append("mercurial>=2.1,<2.2")
67
67
68
68
69 try:
69 try:
70 from rhodecode.lib import get_current_revision
70 from rhodecode.lib import get_current_revision
71 _rev = get_current_revision()
71 _rev = get_current_revision()
72 except ImportError:
72 except ImportError:
73 # this is needed when doing some setup.py operations
73 # this is needed when doing some setup.py operations
74 _rev = False
74 _rev = False
75
75
76 if len(VERSION) > 3 and _rev:
76 if len(VERSION) > 3 and _rev:
77 __version__ += ' [rev:%s]' % _rev[0]
77 __version__ += ' [rev:%s]' % _rev[0]
78
78
79
79
80 def get_version():
80 def get_version():
81 """Returns shorter version (digit parts only) as string."""
81 """Returns shorter version (digit parts only) as string."""
82
82
83 return '.'.join((str(each) for each in VERSION[:3]))
83 return '.'.join((str(each) for each in VERSION[:3]))
84
84
85 BACKENDS = {
85 BACKENDS = {
86 'hg': 'Mercurial repository',
86 'hg': 'Mercurial repository',
87 'git': 'Git repository',
87 'git': 'Git repository',
88 }
88 }
89
89
90 CELERY_ON = False
90 CELERY_ON = False
91
91
92 # link to config for pylons
92 # link to config for pylons
93 CONFIG = None
93 CONFIG = None
@@ -1,121 +1,126 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.search
3 rhodecode.controllers.search
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Search controller for rhodecode
6 Search controller for rhodecode
7
7
8 :created_on: Aug 7, 2010
8 :created_on: Aug 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import traceback
26 import traceback
27
27
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from pylons import request, config, session, tmpl_context as c
29 from pylons import request, config, tmpl_context as c
30
30
31 from rhodecode.lib.auth import LoginRequired
31 from rhodecode.lib.auth import LoginRequired
32 from rhodecode.lib.base import BaseController, render
32 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
33 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
34
34
35 from webhelpers.paginate import Page
35 from webhelpers.paginate import Page
36 from webhelpers.util import update_params
36 from webhelpers.util import update_params
37
37
38 from whoosh.index import open_dir, EmptyIndexError
38 from whoosh.index import open_dir, EmptyIndexError
39 from whoosh.qparser import QueryParser, QueryParserError
39 from whoosh.qparser import QueryParser, QueryParserError
40 from whoosh.query import Phrase, Wildcard, Term, Prefix
40 from whoosh.query import Phrase, Wildcard, Term, Prefix
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 class SearchController(BaseController):
45 class SearchController(BaseController):
46
46
47 @LoginRequired()
47 @LoginRequired()
48 def __before__(self):
48 def __before__(self):
49 super(SearchController, self).__before__()
49 super(SearchController, self).__before__()
50
50
51 def index(self, search_repo=None):
51 def index(self, search_repo=None):
52 c.repo_name = search_repo
52 c.repo_name = search_repo
53 c.formated_results = []
53 c.formated_results = []
54 c.runtime = ''
54 c.runtime = ''
55 c.cur_query = request.GET.get('q', None)
55 c.cur_query = request.GET.get('q', None)
56 c.cur_type = request.GET.get('type', 'source')
56 c.cur_type = request.GET.get('type', 'source')
57 c.cur_search = search_type = {'content': 'content',
57 c.cur_search = search_type = {'content': 'content',
58 'commit': 'content',
58 'commit': 'content',
59 'path': 'path',
59 'path': 'path',
60 'repository': 'repository'}\
60 'repository': 'repository'}\
61 .get(c.cur_type, 'content')
61 .get(c.cur_type, 'content')
62
62
63 if c.cur_query:
63 if c.cur_query:
64 cur_query = c.cur_query.lower()
64 cur_query = c.cur_query.lower()
65
65
66 if c.cur_query:
66 if c.cur_query:
67 p = int(request.params.get('page', 1))
67 p = int(request.params.get('page', 1))
68 highlight_items = set()
68 highlight_items = set()
69 try:
69 try:
70 idx = open_dir(config['app_conf']['index_dir'],
70 idx = open_dir(config['app_conf']['index_dir'],
71 indexname=IDX_NAME)
71 indexname=IDX_NAME)
72 searcher = idx.searcher()
72 searcher = idx.searcher()
73
73
74 qp = QueryParser(search_type, schema=SCHEMA)
74 qp = QueryParser(search_type, schema=SCHEMA)
75 if c.repo_name:
75 if c.repo_name:
76 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
76 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
77 try:
77 try:
78 query = qp.parse(unicode(cur_query))
78 query = qp.parse(unicode(cur_query))
79
79 # extract words for highlight
80 if isinstance(query, Phrase):
80 if isinstance(query, Phrase):
81 highlight_items.update(query.words)
81 highlight_items.update(query.words)
82 elif isinstance(query, Prefix):
82 elif isinstance(query, Prefix):
83 highlight_items.add(query.text)
83 highlight_items.add(query.text)
84 else:
84 else:
85 for i in query.all_terms():
85 for i in query.all_terms():
86 if i[0] == 'content':
86 if i[0] == 'content':
87 highlight_items.add(i[1])
87 highlight_items.add(i[1])
88
88
89 matcher = query.matcher(searcher)
89 matcher = query.matcher(searcher)
90
90
91 log.debug(query)
91 log.debug(query)
92 log.debug(highlight_items)
92 log.debug(highlight_items)
93 results = searcher.search(query)
93 results = searcher.search(query)
94 res_ln = len(results)
94 res_ln = len(results)
95 c.runtime = '%s results (%.3f seconds)' \
95 c.runtime = '%s results (%.3f seconds)' % (
96 % (res_ln, results.runtime)
96 res_ln, results.runtime
97 )
97
98
98 def url_generator(**kw):
99 def url_generator(**kw):
99 return update_params("?q=%s&type=%s" \
100 return update_params("?q=%s&type=%s" \
100 % (c.cur_query, c.cur_search), **kw)
101 % (c.cur_query, c.cur_search), **kw)
101
102
102 c.formated_results = Page(
103 c.formated_results = Page(
103 ResultWrapper(search_type, searcher, matcher,
104 ResultWrapper(search_type, searcher, matcher,
104 highlight_items),
105 highlight_items),
105 page=p, item_count=res_ln,
106 page=p,
106 items_per_page=10, url=url_generator)
107 item_count=res_ln,
108 items_per_page=10,
109 url=url_generator
110 )
107
111
108 except QueryParserError:
112 except QueryParserError:
109 c.runtime = _('Invalid search query. Try quoting it.')
113 c.runtime = _('Invalid search query. Try quoting it.')
110 searcher.close()
114 searcher.close()
111 except (EmptyIndexError, IOError):
115 except (EmptyIndexError, IOError):
112 log.error(traceback.format_exc())
116 log.error(traceback.format_exc())
113 log.error('Empty Index data')
117 log.error('Empty Index data')
114 c.runtime = _('There is no index to search in. '
118 c.runtime = _('There is no index to search in. '
115 'Please run whoosh indexer')
119 'Please run whoosh indexer')
116 except (Exception):
120 except (Exception):
117 log.error(traceback.format_exc())
121 log.error(traceback.format_exc())
118 c.runtime = _('An error occurred during this search operation')
122 c.runtime = _('An error occurred during this search operation')
119
123
124
120 # Return a rendered template
125 # Return a rendered template
121 return render('/search/search.html')
126 return render('/search/search.html')
@@ -1,226 +1,229 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.indexers.__init__
3 rhodecode.lib.indexers.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Whoosh indexing module for RhodeCode
6 Whoosh indexing module for RhodeCode
7
7
8 :created_on: Aug 17, 2010
8 :created_on: Aug 17, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
25 import os
26 import sys
26 import sys
27 import traceback
27 import traceback
28 from os.path import dirname as dn, join as jn
28 from os.path import dirname as dn, join as jn
29
29
30 #to get the rhodecode import
30 #to get the rhodecode import
31 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
31 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
32
32
33 from string import strip
33 from string import strip
34 from shutil import rmtree
34 from shutil import rmtree
35
35
36 from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
36 from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
37 from whoosh.fields import TEXT, ID, STORED, Schema, FieldType
37 from whoosh.fields import TEXT, ID, STORED, Schema, FieldType
38 from whoosh.index import create_in, open_dir
38 from whoosh.index import create_in, open_dir
39 from whoosh.formats import Characters
39 from whoosh.formats import Characters
40 from whoosh.highlight import highlight, SimpleFragmenter, HtmlFormatter
40 from whoosh.highlight import highlight, HtmlFormatter, ContextFragmenter
41
41
42 from webhelpers.html.builder import escape
42 from webhelpers.html.builder import escape
43 from sqlalchemy import engine_from_config
43 from sqlalchemy import engine_from_config
44 from vcs.utils.lazy import LazyProperty
45
44
46 from rhodecode.model import init_model
45 from rhodecode.model import init_model
47 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
48 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
49 from rhodecode.config.environment import load_environment
48 from rhodecode.config.environment import load_environment
50 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP
49 from rhodecode.lib import LANGUAGES_EXTENSIONS_MAP, LazyProperty
51 from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
50 from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
52
51
53 #EXTENSIONS WE WANT TO INDEX CONTENT OFF
52 # EXTENSIONS WE WANT TO INDEX CONTENT OFF
54 INDEX_EXTENSIONS = LANGUAGES_EXTENSIONS_MAP.keys()
53 INDEX_EXTENSIONS = LANGUAGES_EXTENSIONS_MAP.keys()
55
54
56 #CUSTOM ANALYZER wordsplit + lowercase filter
55 # CUSTOM ANALYZER wordsplit + lowercase filter
57 ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
56 ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
58
57
59
58
60 #INDEX SCHEMA DEFINITION
59 #INDEX SCHEMA DEFINITION
61 SCHEMA = Schema(owner=TEXT(),
60 SCHEMA = Schema(
62 repository=TEXT(stored=True),
61 owner=TEXT(),
63 path=TEXT(stored=True),
62 repository=TEXT(stored=True),
64 content=FieldType(format=Characters(ANALYZER),
63 path=TEXT(stored=True),
65 scorable=True, stored=True),
64 content=FieldType(format=Characters(), analyzer=ANALYZER,
66 modtime=STORED(), extension=TEXT(stored=True))
65 scorable=True, stored=True),
67
66 modtime=STORED(),
67 extension=TEXT(stored=True)
68 )
68
69
69 IDX_NAME = 'HG_INDEX'
70 IDX_NAME = 'HG_INDEX'
70 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
71 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
71 FRAGMENTER = SimpleFragmenter(200)
72 FRAGMENTER = ContextFragmenter(200)
72
73
73
74
74 class MakeIndex(BasePasterCommand):
75 class MakeIndex(BasePasterCommand):
75
76
76 max_args = 1
77 max_args = 1
77 min_args = 1
78 min_args = 1
78
79
79 usage = "CONFIG_FILE"
80 usage = "CONFIG_FILE"
80 summary = "Creates index for full text search given configuration file"
81 summary = "Creates index for full text search given configuration file"
81 group_name = "RhodeCode"
82 group_name = "RhodeCode"
82 takes_config_file = -1
83 takes_config_file = -1
83 parser = Command.standard_parser(verbose=True)
84 parser = Command.standard_parser(verbose=True)
84
85
85 def command(self):
86 def command(self):
86
87
87 from pylons import config
88 from pylons import config
88 add_cache(config)
89 add_cache(config)
89 engine = engine_from_config(config, 'sqlalchemy.db1.')
90 engine = engine_from_config(config, 'sqlalchemy.db1.')
90 init_model(engine)
91 init_model(engine)
91
92
92 index_location = config['index_dir']
93 index_location = config['index_dir']
93 repo_location = self.options.repo_location \
94 repo_location = self.options.repo_location \
94 if self.options.repo_location else RepoModel().repos_path
95 if self.options.repo_location else RepoModel().repos_path
95 repo_list = map(strip, self.options.repo_list.split(',')) \
96 repo_list = map(strip, self.options.repo_list.split(',')) \
96 if self.options.repo_list else None
97 if self.options.repo_list else None
97
98
98 #======================================================================
99 #======================================================================
99 # WHOOSH DAEMON
100 # WHOOSH DAEMON
100 #======================================================================
101 #======================================================================
101 from rhodecode.lib.pidlock import LockHeld, DaemonLock
102 from rhodecode.lib.pidlock import LockHeld, DaemonLock
102 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
103 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
103 try:
104 try:
104 l = DaemonLock(file_=jn(dn(dn(index_location)), 'make_index.lock'))
105 l = DaemonLock(file_=jn(dn(dn(index_location)), 'make_index.lock'))
105 WhooshIndexingDaemon(index_location=index_location,
106 WhooshIndexingDaemon(index_location=index_location,
106 repo_location=repo_location,
107 repo_location=repo_location,
107 repo_list=repo_list)\
108 repo_list=repo_list)\
108 .run(full_index=self.options.full_index)
109 .run(full_index=self.options.full_index)
109 l.release()
110 l.release()
110 except LockHeld:
111 except LockHeld:
111 sys.exit(1)
112 sys.exit(1)
112
113
113 def update_parser(self):
114 def update_parser(self):
114 self.parser.add_option('--repo-location',
115 self.parser.add_option('--repo-location',
115 action='store',
116 action='store',
116 dest='repo_location',
117 dest='repo_location',
117 help="Specifies repositories location to index OPTIONAL",
118 help="Specifies repositories location to index OPTIONAL",
118 )
119 )
119 self.parser.add_option('--index-only',
120 self.parser.add_option('--index-only',
120 action='store',
121 action='store',
121 dest='repo_list',
122 dest='repo_list',
122 help="Specifies a comma separated list of repositores "
123 help="Specifies a comma separated list of repositores "
123 "to build index on OPTIONAL",
124 "to build index on OPTIONAL",
124 )
125 )
125 self.parser.add_option('-f',
126 self.parser.add_option('-f',
126 action='store_true',
127 action='store_true',
127 dest='full_index',
128 dest='full_index',
128 help="Specifies that index should be made full i.e"
129 help="Specifies that index should be made full i.e"
129 " destroy old and build from scratch",
130 " destroy old and build from scratch",
130 default=False)
131 default=False)
131
132
132
133
133 class ResultWrapper(object):
134 class ResultWrapper(object):
134 def __init__(self, search_type, searcher, matcher, highlight_items):
135 def __init__(self, search_type, searcher, matcher, highlight_items):
135 self.search_type = search_type
136 self.search_type = search_type
136 self.searcher = searcher
137 self.searcher = searcher
137 self.matcher = matcher
138 self.matcher = matcher
138 self.highlight_items = highlight_items
139 self.highlight_items = highlight_items
139 self.fragment_size = 200 / 2
140 self.fragment_size = 200
140
141
141 @LazyProperty
142 @LazyProperty
142 def doc_ids(self):
143 def doc_ids(self):
143 docs_id = []
144 docs_id = []
144 while self.matcher.is_active():
145 while self.matcher.is_active():
145 docnum = self.matcher.id()
146 docnum = self.matcher.id()
146 chunks = [offsets for offsets in self.get_chunks()]
147 chunks = [offsets for offsets in self.get_chunks()]
147 docs_id.append([docnum, chunks])
148 docs_id.append([docnum, chunks])
148 self.matcher.next()
149 self.matcher.next()
149 return docs_id
150 return docs_id
150
151
151 def __str__(self):
152 def __str__(self):
152 return '<%s at %s>' % (self.__class__.__name__, len(self.doc_ids))
153 return '<%s at %s>' % (self.__class__.__name__, len(self.doc_ids))
153
154
154 def __repr__(self):
155 def __repr__(self):
155 return self.__str__()
156 return self.__str__()
156
157
157 def __len__(self):
158 def __len__(self):
158 return len(self.doc_ids)
159 return len(self.doc_ids)
159
160
160 def __iter__(self):
161 def __iter__(self):
161 """
162 """
162 Allows Iteration over results,and lazy generate content
163 Allows Iteration over results,and lazy generate content
163
164
164 *Requires* implementation of ``__getitem__`` method.
165 *Requires* implementation of ``__getitem__`` method.
165 """
166 """
166 for docid in self.doc_ids:
167 for docid in self.doc_ids:
167 yield self.get_full_content(docid)
168 yield self.get_full_content(docid)
168
169
169 def __getitem__(self, key):
170 def __getitem__(self, key):
170 """
171 """
171 Slicing of resultWrapper
172 Slicing of resultWrapper
172 """
173 """
173 i, j = key.start, key.stop
174 i, j = key.start, key.stop
174
175
175 slice = []
176 slices = []
176 for docid in self.doc_ids[i:j]:
177 for docid in self.doc_ids[i:j]:
177 slice.append(self.get_full_content(docid))
178 slices.append(self.get_full_content(docid))
178 return slice
179 return slices
179
180
180 def get_full_content(self, docid):
181 def get_full_content(self, docid):
181 res = self.searcher.stored_fields(docid[0])
182 res = self.searcher.stored_fields(docid[0])
182 f_path = res['path'][res['path'].find(res['repository']) \
183 f_path = res['path'][res['path'].find(res['repository']) \
183 + len(res['repository']):].lstrip('/')
184 + len(res['repository']):].lstrip('/')
184
185
185 content_short = self.get_short_content(res, docid[1])
186 content_short = self.get_short_content(res, docid[1])
186 res.update({'content_short':content_short,
187 res.update({'content_short': content_short,
187 'content_short_hl':self.highlight(content_short),
188 'content_short_hl': self.highlight(content_short),
188 'f_path':f_path})
189 'f_path': f_path})
189
190
190 return res
191 return res
191
192
192 def get_short_content(self, res, chunks):
193 def get_short_content(self, res, chunks):
193
194
194 return ''.join([res['content'][chunk[0]:chunk[1]] for chunk in chunks])
195 return ''.join([res['content'][chunk[0]:chunk[1]] for chunk in chunks])
195
196
196 def get_chunks(self):
197 def get_chunks(self):
197 """
198 """
198 Smart function that implements chunking the content
199 Smart function that implements chunking the content
199 but not overlap chunks so it doesn't highlight the same
200 but not overlap chunks so it doesn't highlight the same
200 close occurrences twice.
201 close occurrences twice.
201
202
202 :param matcher:
203 :param matcher:
203 :param size:
204 :param size:
204 """
205 """
205 memory = [(0, 0)]
206 memory = [(0, 0)]
206 for span in self.matcher.spans():
207 for span in self.matcher.spans():
207 start = span.startchar or 0
208 start = span.startchar or 0
208 end = span.endchar or 0
209 end = span.endchar or 0
209 start_offseted = max(0, start - self.fragment_size)
210 start_offseted = max(0, start - self.fragment_size)
210 end_offseted = end + self.fragment_size
211 end_offseted = end + self.fragment_size
211
212
212 if start_offseted < memory[-1][1]:
213 if start_offseted < memory[-1][1]:
213 start_offseted = memory[-1][1]
214 start_offseted = memory[-1][1]
214 memory.append((start_offseted, end_offseted,))
215 memory.append((start_offseted, end_offseted,))
215 yield (start_offseted, end_offseted,)
216 yield (start_offseted, end_offseted,)
216
217
217 def highlight(self, content, top=5):
218 def highlight(self, content, top=5):
218 if self.search_type != 'content':
219 if self.search_type != 'content':
219 return ''
220 return ''
220 hl = highlight(escape(content),
221 hl = highlight(
221 self.highlight_items,
222 text=escape(content),
222 analyzer=ANALYZER,
223 terms=self.highlight_items,
223 fragmenter=FRAGMENTER,
224 analyzer=ANALYZER,
224 formatter=FORMATTER,
225 fragmenter=FRAGMENTER,
225 top=top)
226 formatter=FORMATTER,
227 top=top
228 )
226 return hl
229 return hl
@@ -1,238 +1,235 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.indexers.daemon
3 rhodecode.lib.indexers.daemon
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 A daemon will read from task table and run tasks
6 A daemon will read from task table and run tasks
7
7
8 :created_on: Jan 26, 2010
8 :created_on: Jan 26, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import sys
27 import sys
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from shutil import rmtree
31 from shutil import rmtree
32 from time import mktime
32 from time import mktime
33
33
34 from os.path import dirname as dn
34 from os.path import dirname as dn
35 from os.path import join as jn
35 from os.path import join as jn
36
36
37 #to get the rhodecode import
37 #to get the rhodecode import
38 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
38 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
39 sys.path.append(project_path)
39 sys.path.append(project_path)
40
40
41
41
42 from rhodecode.model.scm import ScmModel
42 from rhodecode.model.scm import ScmModel
43 from rhodecode.lib import safe_unicode
43 from rhodecode.lib import safe_unicode
44 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
44 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
45
45
46 from vcs.exceptions import ChangesetError, RepositoryError, \
46 from vcs.exceptions import ChangesetError, RepositoryError, \
47 NodeDoesNotExistError
47 NodeDoesNotExistError
48
48
49 from whoosh.index import create_in, open_dir
49 from whoosh.index import create_in, open_dir
50
50
51
51
52
53 log = logging.getLogger('whooshIndexer')
52 log = logging.getLogger('whooshIndexer')
54 # create logger
53 # create logger
55 log.setLevel(logging.DEBUG)
54 log.setLevel(logging.DEBUG)
56 log.propagate = False
55 log.propagate = False
57 # create console handler and set level to debug
56 # create console handler and set level to debug
58 ch = logging.StreamHandler()
57 ch = logging.StreamHandler()
59 ch.setLevel(logging.DEBUG)
58 ch.setLevel(logging.DEBUG)
60
59
61 # create formatter
60 # create formatter
62 formatter = logging.Formatter("%(asctime)s - %(name)s -"
61 formatter = logging.Formatter("%(asctime)s - %(name)s -"
63 " %(levelname)s - %(message)s")
62 " %(levelname)s - %(message)s")
64
63
65 # add formatter to ch
64 # add formatter to ch
66 ch.setFormatter(formatter)
65 ch.setFormatter(formatter)
67
66
68 # add ch to logger
67 # add ch to logger
69 log.addHandler(ch)
68 log.addHandler(ch)
70
69
70
71 class WhooshIndexingDaemon(object):
71 class WhooshIndexingDaemon(object):
72 """
72 """
73 Daemon for atomic jobs
73 Daemon for atomic jobs
74 """
74 """
75
75
76 def __init__(self, indexname='HG_INDEX', index_location=None,
76 def __init__(self, indexname=IDX_NAME, index_location=None,
77 repo_location=None, sa=None, repo_list=None):
77 repo_location=None, sa=None, repo_list=None):
78 self.indexname = indexname
78 self.indexname = indexname
79
79
80 self.index_location = index_location
80 self.index_location = index_location
81 if not index_location:
81 if not index_location:
82 raise Exception('You have to provide index location')
82 raise Exception('You have to provide index location')
83
83
84 self.repo_location = repo_location
84 self.repo_location = repo_location
85 if not repo_location:
85 if not repo_location:
86 raise Exception('You have to provide repositories location')
86 raise Exception('You have to provide repositories location')
87
87
88 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location)
88 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location)
89
89
90 if repo_list:
90 if repo_list:
91 filtered_repo_paths = {}
91 filtered_repo_paths = {}
92 for repo_name, repo in self.repo_paths.items():
92 for repo_name, repo in self.repo_paths.items():
93 if repo_name in repo_list:
93 if repo_name in repo_list:
94 filtered_repo_paths[repo_name] = repo
94 filtered_repo_paths[repo_name] = repo
95
95
96 self.repo_paths = filtered_repo_paths
96 self.repo_paths = filtered_repo_paths
97
97
98
99 self.initial = False
98 self.initial = False
100 if not os.path.isdir(self.index_location):
99 if not os.path.isdir(self.index_location):
101 os.makedirs(self.index_location)
100 os.makedirs(self.index_location)
102 log.info('Cannot run incremental index since it does not'
101 log.info('Cannot run incremental index since it does not'
103 ' yet exist running full build')
102 ' yet exist running full build')
104 self.initial = True
103 self.initial = True
105
104
106 def get_paths(self, repo):
105 def get_paths(self, repo):
107 """recursive walk in root dir and return a set of all path in that dir
106 """recursive walk in root dir and return a set of all path in that dir
108 based on repository walk function
107 based on repository walk function
109 """
108 """
110 index_paths_ = set()
109 index_paths_ = set()
111 try:
110 try:
112 tip = repo.get_changeset('tip')
111 tip = repo.get_changeset('tip')
113 for topnode, dirs, files in tip.walk('/'):
112 for topnode, dirs, files in tip.walk('/'):
114 for f in files:
113 for f in files:
115 index_paths_.add(jn(repo.path, f.path))
114 index_paths_.add(jn(repo.path, f.path))
116
115
117 except RepositoryError, e:
116 except RepositoryError, e:
118 log.debug(traceback.format_exc())
117 log.debug(traceback.format_exc())
119 pass
118 pass
120 return index_paths_
119 return index_paths_
121
120
122 def get_node(self, repo, path):
121 def get_node(self, repo, path):
123 n_path = path[len(repo.path) + 1:]
122 n_path = path[len(repo.path) + 1:]
124 node = repo.get_changeset().get_node(n_path)
123 node = repo.get_changeset().get_node(n_path)
125 return node
124 return node
126
125
127 def get_node_mtime(self, node):
126 def get_node_mtime(self, node):
128 return mktime(node.last_changeset.date.timetuple())
127 return mktime(node.last_changeset.date.timetuple())
129
128
130 def add_doc(self, writer, path, repo, repo_name):
129 def add_doc(self, writer, path, repo, repo_name):
131 """Adding doc to writer this function itself fetches data from
130 """Adding doc to writer this function itself fetches data from
132 the instance of vcs backend"""
131 the instance of vcs backend"""
133 node = self.get_node(repo, path)
132 node = self.get_node(repo, path)
134
133
135 #we just index the content of chosen files, and skip binary files
134 #we just index the content of chosen files, and skip binary files
136 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
135 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
137
136
138 u_content = node.content
137 u_content = node.content
139 if not isinstance(u_content, unicode):
138 if not isinstance(u_content, unicode):
140 log.warning(' >> %s Could not get this content as unicode '
139 log.warning(' >> %s Could not get this content as unicode '
141 'replacing with empty content', path)
140 'replacing with empty content', path)
142 u_content = u''
141 u_content = u''
143 else:
142 else:
144 log.debug(' >> %s [WITH CONTENT]' % path)
143 log.debug(' >> %s [WITH CONTENT]' % path)
145
144
146 else:
145 else:
147 log.debug(' >> %s' % path)
146 log.debug(' >> %s' % path)
148 #just index file name without it's content
147 #just index file name without it's content
149 u_content = u''
148 u_content = u''
150
149
151 writer.add_document(owner=unicode(repo.contact),
150 writer.add_document(owner=unicode(repo.contact),
152 repository=safe_unicode(repo_name),
151 repository=safe_unicode(repo_name),
153 path=safe_unicode(path),
152 path=safe_unicode(path),
154 content=u_content,
153 content=u_content,
155 modtime=self.get_node_mtime(node),
154 modtime=self.get_node_mtime(node),
156 extension=node.extension)
155 extension=node.extension)
157
156
158
159 def build_index(self):
157 def build_index(self):
160 if os.path.exists(self.index_location):
158 if os.path.exists(self.index_location):
161 log.debug('removing previous index')
159 log.debug('removing previous index')
162 rmtree(self.index_location)
160 rmtree(self.index_location)
163
161
164 if not os.path.exists(self.index_location):
162 if not os.path.exists(self.index_location):
165 os.mkdir(self.index_location)
163 os.mkdir(self.index_location)
166
164
167 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
165 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
168 writer = idx.writer()
166 writer = idx.writer()
169
167
170 for repo_name, repo in self.repo_paths.items():
168 for repo_name, repo in self.repo_paths.items():
171 log.debug('building index @ %s' % repo.path)
169 log.debug('building index @ %s' % repo.path)
172
170
173 for idx_path in self.get_paths(repo):
171 for idx_path in self.get_paths(repo):
174 self.add_doc(writer, idx_path, repo, repo_name)
172 self.add_doc(writer, idx_path, repo, repo_name)
175
173
176 log.debug('>> COMMITING CHANGES <<')
174 log.debug('>> COMMITING CHANGES <<')
177 writer.commit(merge=True)
175 writer.commit(merge=True)
178 log.debug('>>> FINISHED BUILDING INDEX <<<')
176 log.debug('>>> FINISHED BUILDING INDEX <<<')
179
177
180
181 def update_index(self):
178 def update_index(self):
182 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
179 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
183
180
184 idx = open_dir(self.index_location, indexname=self.indexname)
181 idx = open_dir(self.index_location, indexname=self.indexname)
185 # The set of all paths in the index
182 # The set of all paths in the index
186 indexed_paths = set()
183 indexed_paths = set()
187 # The set of all paths we need to re-index
184 # The set of all paths we need to re-index
188 to_index = set()
185 to_index = set()
189
186
190 reader = idx.reader()
187 reader = idx.reader()
191 writer = idx.writer()
188 writer = idx.writer()
192
189
193 # Loop over the stored fields in the index
190 # Loop over the stored fields in the index
194 for fields in reader.all_stored_fields():
191 for fields in reader.all_stored_fields():
195 indexed_path = fields['path']
192 indexed_path = fields['path']
196 indexed_paths.add(indexed_path)
193 indexed_paths.add(indexed_path)
197
194
198 repo = self.repo_paths[fields['repository']]
195 repo = self.repo_paths[fields['repository']]
199
196
200 try:
197 try:
201 node = self.get_node(repo, indexed_path)
198 node = self.get_node(repo, indexed_path)
202 except (ChangesetError, NodeDoesNotExistError):
199 except (ChangesetError, NodeDoesNotExistError):
203 # This file was deleted since it was indexed
200 # This file was deleted since it was indexed
204 log.debug('removing from index %s' % indexed_path)
201 log.debug('removing from index %s' % indexed_path)
205 writer.delete_by_term('path', indexed_path)
202 writer.delete_by_term('path', indexed_path)
206
203
207 else:
204 else:
208 # Check if this file was changed since it was indexed
205 # Check if this file was changed since it was indexed
209 indexed_time = fields['modtime']
206 indexed_time = fields['modtime']
210 mtime = self.get_node_mtime(node)
207 mtime = self.get_node_mtime(node)
211 if mtime > indexed_time:
208 if mtime > indexed_time:
212 # The file has changed, delete it and add it to the list of
209 # The file has changed, delete it and add it to the list of
213 # files to reindex
210 # files to reindex
214 log.debug('adding to reindex list %s' % indexed_path)
211 log.debug('adding to reindex list %s' % indexed_path)
215 writer.delete_by_term('path', indexed_path)
212 writer.delete_by_term('path', indexed_path)
216 to_index.add(indexed_path)
213 to_index.add(indexed_path)
217
214
218 # Loop over the files in the filesystem
215 # Loop over the files in the filesystem
219 # Assume we have a function that gathers the filenames of the
216 # Assume we have a function that gathers the filenames of the
220 # documents to be indexed
217 # documents to be indexed
221 for repo_name, repo in self.repo_paths.items():
218 for repo_name, repo in self.repo_paths.items():
222 for path in self.get_paths(repo):
219 for path in self.get_paths(repo):
223 if path in to_index or path not in indexed_paths:
220 if path in to_index or path not in indexed_paths:
224 # This is either a file that's changed, or a new file
221 # This is either a file that's changed, or a new file
225 # that wasn't indexed before. So index it!
222 # that wasn't indexed before. So index it!
226 self.add_doc(writer, path, repo, repo_name)
223 self.add_doc(writer, path, repo, repo_name)
227 log.debug('re indexing %s' % path)
224 log.debug('re indexing %s' % path)
228
225
229 log.debug('>> COMMITING CHANGES <<')
226 log.debug('>> COMMITING CHANGES <<')
230 writer.commit(merge=True)
227 writer.commit(merge=True)
231 log.debug('>>> FINISHED REBUILDING INDEX <<<')
228 log.debug('>>> FINISHED REBUILDING INDEX <<<')
232
229
233 def run(self, full_index=False):
230 def run(self, full_index=False):
234 """Run daemon"""
231 """Run daemon"""
235 if full_index or self.initial:
232 if full_index or self.initial:
236 self.build_index()
233 self.build_index()
237 else:
234 else:
238 self.update_index()
235 self.update_index()
@@ -1,159 +1,169 b''
1 div.codeblock {
1 div.codeblock {
2 overflow: auto;
2 overflow: auto;
3 padding: 0px;
3 padding: 0px;
4 border: 1px solid #ccc;
4 border: 1px solid #ccc;
5 background: #f8f8f8;
5 background: #f8f8f8;
6 font-size: 100%;
6 font-size: 100%;
7 line-height: 100%;
7 line-height: 100%;
8 /* new */
8 /* new */
9 line-height: 125%;
9 line-height: 125%;
10 -webkit-border-radius: 4px;
10 -webkit-border-radius: 4px;
11 -moz-border-radius: 4px;
11 -moz-border-radius: 4px;
12 border-radius: 4px;
12 border-radius: 4px;
13 }
13 }
14 div.codeblock .code-header{
14 div.codeblock .code-header{
15 border-bottom: 1px solid #CCCCCC;
15 border-bottom: 1px solid #CCCCCC;
16 background: #EEEEEE;
16 background: #EEEEEE;
17 padding:10px 0 10px 0;
17 padding:10px 0 10px 0;
18 }
18 }
19
19
20 div.codeblock .code-header .stats{
20 div.codeblock .code-header .stats{
21 clear: both;
21 clear: both;
22 margin-top:-3px;
22 margin-top:-3px;
23 padding-left: 8px;
23 padding-left: 8px;
24 border-bottom: 1px solid rgb(204, 204, 204);
24 border-bottom: 1px solid rgb(204, 204, 204);
25 margin-bottom: 5px; height: 23px;
25 margin-bottom: 5px; height: 23px;
26 }
26 }
27
27
28 div.codeblock .code-header .stats .left{
28 div.codeblock .code-header .stats .left{
29 float:left;
29 float:left;
30 }
30 }
31 div.codeblock .code-header .stats .left.item{
31 div.codeblock .code-header .stats .left.item{
32 float:left;
32 float:left;
33 padding: 0 9px 0 9px;
33 padding: 0 9px 0 9px;
34 border-right:1px solid #ccc;
34 border-right:1px solid #ccc;
35 }
35 }
36 div.codeblock .code-header .stats .left.item.last{
36 div.codeblock .code-header .stats .left.item.last{
37 border-right:none;
37 border-right:none;
38 }
38 }
39 div.codeblock .code-header .stats .buttons{
39 div.codeblock .code-header .stats .buttons{
40 float:right;
40 float:right;
41 padding-right:4px;
41 padding-right:4px;
42 }
42 }
43
43
44 div.codeblock .code-header .author{
44 div.codeblock .code-header .author{
45 margin-left:25px;
45 margin-left:25px;
46 font-weight: bold;
46 font-weight: bold;
47 height: 25px;
47 height: 25px;
48 }
48 }
49 div.codeblock .code-header .author .user{
49 div.codeblock .code-header .author .user{
50 padding-top:3px;
50 padding-top:3px;
51 }
51 }
52 div.codeblock .code-header .commit{
52 div.codeblock .code-header .commit{
53 margin-left:25px;
53 margin-left:25px;
54 font-weight: normal;
54 font-weight: normal;
55 white-space:pre;
55 white-space:pre;
56 }
56 }
57
57
58 div.codeblock .code-body table{
58 div.codeblock .code-body table{
59 width: 0 !important;
59 width: 0 !important;
60 border: 0px !important;
60 border: 0px !important;
61 }
61 }
62 div.codeblock .code-body table td {
62 div.codeblock .code-body table td {
63 border: 0px !important;
63 border: 0px !important;
64 }
64 }
65 div.code-body {
65 div.code-body {
66 background-color: #FFFFFF;
66 background-color: #FFFFFF;
67 }
67 }
68 div.code-body pre .match{
68
69 div.codeblock .code-header .search-path {
70 padding: 0px 0px 0px 10px;
71 }
72
73 div.search-code-body {
74 background-color: #FFFFFF;
75 padding: 5px 0px 5px 10px;
76 }
77
78 div.search-code-body pre .match{
69 background-color: #FAFFA6;
79 background-color: #FAFFA6;
70 }
80 }
71 div.code-body pre .break{
81 div.search-code-body pre .break{
72 background-color: #DDE7EF;
82 background-color: #DDE7EF;
73 width: 100%;
83 width: 100%;
74 color: #747474;
84 color: #747474;
75 display: block;
85 display: block;
76
86
77 }
87 }
78 div.annotatediv{
88 div.annotatediv{
79 margin-left:2px;
89 margin-left:2px;
80 margin-right:4px;
90 margin-right:4px;
81 }
91 }
82 .code-highlight {
92 .code-highlight {
83 padding: 0px;
93 padding: 0px;
84 margin-top: 5px;
94 margin-top: 5px;
85 margin-bottom: 5px;
95 margin-bottom: 5px;
86 border-left: 2px solid #ccc;
96 border-left: 2px solid #ccc;
87 }
97 }
88 .code-highlight pre, .linenodiv pre {
98 .code-highlight pre, .linenodiv pre {
89 padding: 5px;
99 padding: 5px;
90 margin: 0;
100 margin: 0;
91 }
101 }
92 .code-highlight pre div:target {
102 .code-highlight pre div:target {
93 background-color: #FFFFBE !important;
103 background-color: #FFFFBE !important;
94 }
104 }
95
105
96 .linenos a { text-decoration: none; }
106 .linenos a { text-decoration: none; }
97
107
98 .code { display: block; }
108 .code { display: block; }
99 .code-highlight .hll { background-color: #ffffcc }
109 .code-highlight .hll { background-color: #ffffcc }
100 .code-highlight .c { color: #408080; font-style: italic } /* Comment */
110 .code-highlight .c { color: #408080; font-style: italic } /* Comment */
101 .code-highlight .err { border: 1px solid #FF0000 } /* Error */
111 .code-highlight .err { border: 1px solid #FF0000 } /* Error */
102 .code-highlight .k { color: #008000; font-weight: bold } /* Keyword */
112 .code-highlight .k { color: #008000; font-weight: bold } /* Keyword */
103 .code-highlight .o { color: #666666 } /* Operator */
113 .code-highlight .o { color: #666666 } /* Operator */
104 .code-highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
114 .code-highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
105 .code-highlight .cp { color: #BC7A00 } /* Comment.Preproc */
115 .code-highlight .cp { color: #BC7A00 } /* Comment.Preproc */
106 .code-highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
116 .code-highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
107 .code-highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
117 .code-highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
108 .code-highlight .gd { color: #A00000 } /* Generic.Deleted */
118 .code-highlight .gd { color: #A00000 } /* Generic.Deleted */
109 .code-highlight .ge { font-style: italic } /* Generic.Emph */
119 .code-highlight .ge { font-style: italic } /* Generic.Emph */
110 .code-highlight .gr { color: #FF0000 } /* Generic.Error */
120 .code-highlight .gr { color: #FF0000 } /* Generic.Error */
111 .code-highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
121 .code-highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
112 .code-highlight .gi { color: #00A000 } /* Generic.Inserted */
122 .code-highlight .gi { color: #00A000 } /* Generic.Inserted */
113 .code-highlight .go { color: #808080 } /* Generic.Output */
123 .code-highlight .go { color: #808080 } /* Generic.Output */
114 .code-highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
124 .code-highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
115 .code-highlight .gs { font-weight: bold } /* Generic.Strong */
125 .code-highlight .gs { font-weight: bold } /* Generic.Strong */
116 .code-highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
126 .code-highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
117 .code-highlight .gt { color: #0040D0 } /* Generic.Traceback */
127 .code-highlight .gt { color: #0040D0 } /* Generic.Traceback */
118 .code-highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
128 .code-highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
119 .code-highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
129 .code-highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
120 .code-highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
130 .code-highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
121 .code-highlight .kp { color: #008000 } /* Keyword.Pseudo */
131 .code-highlight .kp { color: #008000 } /* Keyword.Pseudo */
122 .code-highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
132 .code-highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
123 .code-highlight .kt { color: #B00040 } /* Keyword.Type */
133 .code-highlight .kt { color: #B00040 } /* Keyword.Type */
124 .code-highlight .m { color: #666666 } /* Literal.Number */
134 .code-highlight .m { color: #666666 } /* Literal.Number */
125 .code-highlight .s { color: #BA2121 } /* Literal.String */
135 .code-highlight .s { color: #BA2121 } /* Literal.String */
126 .code-highlight .na { color: #7D9029 } /* Name.Attribute */
136 .code-highlight .na { color: #7D9029 } /* Name.Attribute */
127 .code-highlight .nb { color: #008000 } /* Name.Builtin */
137 .code-highlight .nb { color: #008000 } /* Name.Builtin */
128 .code-highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
138 .code-highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
129 .code-highlight .no { color: #880000 } /* Name.Constant */
139 .code-highlight .no { color: #880000 } /* Name.Constant */
130 .code-highlight .nd { color: #AA22FF } /* Name.Decorator */
140 .code-highlight .nd { color: #AA22FF } /* Name.Decorator */
131 .code-highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
141 .code-highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
132 .code-highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
142 .code-highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
133 .code-highlight .nf { color: #0000FF } /* Name.Function */
143 .code-highlight .nf { color: #0000FF } /* Name.Function */
134 .code-highlight .nl { color: #A0A000 } /* Name.Label */
144 .code-highlight .nl { color: #A0A000 } /* Name.Label */
135 .code-highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
145 .code-highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
136 .code-highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
146 .code-highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
137 .code-highlight .nv { color: #19177C } /* Name.Variable */
147 .code-highlight .nv { color: #19177C } /* Name.Variable */
138 .code-highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
148 .code-highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
139 .code-highlight .w { color: #bbbbbb } /* Text.Whitespace */
149 .code-highlight .w { color: #bbbbbb } /* Text.Whitespace */
140 .code-highlight .mf { color: #666666 } /* Literal.Number.Float */
150 .code-highlight .mf { color: #666666 } /* Literal.Number.Float */
141 .code-highlight .mh { color: #666666 } /* Literal.Number.Hex */
151 .code-highlight .mh { color: #666666 } /* Literal.Number.Hex */
142 .code-highlight .mi { color: #666666 } /* Literal.Number.Integer */
152 .code-highlight .mi { color: #666666 } /* Literal.Number.Integer */
143 .code-highlight .mo { color: #666666 } /* Literal.Number.Oct */
153 .code-highlight .mo { color: #666666 } /* Literal.Number.Oct */
144 .code-highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
154 .code-highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
145 .code-highlight .sc { color: #BA2121 } /* Literal.String.Char */
155 .code-highlight .sc { color: #BA2121 } /* Literal.String.Char */
146 .code-highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
156 .code-highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
147 .code-highlight .s2 { color: #BA2121 } /* Literal.String.Double */
157 .code-highlight .s2 { color: #BA2121 } /* Literal.String.Double */
148 .code-highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
158 .code-highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
149 .code-highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
159 .code-highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
150 .code-highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
160 .code-highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
151 .code-highlight .sx { color: #008000 } /* Literal.String.Other */
161 .code-highlight .sx { color: #008000 } /* Literal.String.Other */
152 .code-highlight .sr { color: #BB6688 } /* Literal.String.Regex */
162 .code-highlight .sr { color: #BB6688 } /* Literal.String.Regex */
153 .code-highlight .s1 { color: #BA2121 } /* Literal.String.Single */
163 .code-highlight .s1 { color: #BA2121 } /* Literal.String.Single */
154 .code-highlight .ss { color: #19177C } /* Literal.String.Symbol */
164 .code-highlight .ss { color: #19177C } /* Literal.String.Symbol */
155 .code-highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
165 .code-highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
156 .code-highlight .vc { color: #19177C } /* Name.Variable.Class */
166 .code-highlight .vc { color: #19177C } /* Name.Variable.Class */
157 .code-highlight .vg { color: #19177C } /* Name.Variable.Global */
167 .code-highlight .vg { color: #19177C } /* Name.Variable.Global */
158 .code-highlight .vi { color: #19177C } /* Name.Variable.Instance */
168 .code-highlight .vi { color: #19177C } /* Name.Variable.Instance */
159 .code-highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
169 .code-highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
@@ -1,31 +1,32 b''
1 ##content highligthing
1 ##content highligthing
2
2
3 %for cnt,sr in enumerate(c.formated_results):
3 %for cnt,sr in enumerate(c.formated_results):
4 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
4 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
5 <div class="table">
5 <div class="table">
6 <div id="body${cnt}" class="codeblock">
6 <div id="body${cnt}" class="codeblock">
7 <div class="code-header">
7 <div class="code-header">
8 <div class="revision">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),
8 <div class="search-path">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),
9 h.url('files_home',repo_name=sr['repository'],revision='tip',f_path=sr['f_path']))}</div>
9 h.url('files_home',repo_name=sr['repository'],revision='tip',f_path=sr['f_path']))}
10 </div>
10 </div>
11 </div>
11 <div class="code-body">
12 <div class="search-code-body">
12 <pre>${h.literal(sr['content_short_hl'])}</pre>
13 <pre>${h.literal(sr['content_short_hl'])}</pre>
13 </div>
14 </div>
14 </div>
15 </div>
15 </div>
16 </div>
16 %else:
17 %else:
17 %if cnt == 0:
18 %if cnt == 0:
18 <div class="table">
19 <div class="table">
19 <div id="body${cnt}" class="codeblock">
20 <div id="body${cnt}" class="codeblock">
20 <div class="error">${_('Permission denied')}</div>
21 <div class="error">${_('Permission denied')}</div>
21 </div>
22 </div>
22 </div>
23 </div>
23 %endif
24 %endif
24
25
25 %endif
26 %endif
26 %endfor
27 %endfor
27 %if c.cur_query and c.formated_results:
28 %if c.cur_query and c.formated_results:
28 <div class="pagination-wh pagination-left">
29 <div class="pagination-wh pagination-left">
29 ${c.formated_results.pager('$link_previous ~2~ $link_next')}
30 ${c.formated_results.pager('$link_previous ~2~ $link_next')}
30 </div>
31 </div>
31 %endif
32 %endif
General Comments 0
You need to be logged in to leave comments. Login now