/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.40.10 by Parth Malwankar
assigned copyright to canonical
1
# Copyright (C) 2010 Canonical Ltd
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
from bzrlib.lazy_import import lazy_import
18
lazy_import(globals(), """
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
19
from fnmatch import fnmatch
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
20
import re
21
0.43.8 by Parth Malwankar
added color for regex pattern.
22
from termcolor import color_string, re_color_string, FG
0.43.4 by Parth Malwankar
initial support for color for fixed string grep.
23
24
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
25
from bzrlib import bzrdir
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
26
from bzrlib.workingtree import WorkingTree
0.40.95 by Parth Malwankar
faster mainline rev grep
27
from bzrlib.revisionspec import RevisionSpec, RevisionSpec_revid, RevisionSpec_revno
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
28
from bzrlib import (
29
    errors,
30
    lazy_regex,
0.40.47 by Parth Malwankar
fixes bug #531336. binary files are now skipped.
31
    osutils,
32
    textfile,
33
    trace,
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
34
    )
35
""")
36
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
37
_terminal_encoding = osutils.get_terminal_encoding()
38
_user_encoding = osutils.get_user_encoding()
39
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
40
0.40.95 by Parth Malwankar
faster mainline rev grep
41
class _RevisionNotLinear(Exception):
42
    """Raised when a revision is not on left-hand history."""
43
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
44
0.40.95 by Parth Malwankar
faster mainline rev grep
45
def _rev_on_mainline(rev_tuple):
46
    """returns True is rev tuple is on mainline"""
47
    if len(rev_tuple) == 1:
48
        return True
49
    return rev_tuple[1] == 0 and rev_tuple[2] == 0
50
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
51
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
52
# NOTE: _linear_view_revisions is basided on
53
# bzrlib.log._linear_view_revisions.
54
# This should probably be a common public API
0.40.95 by Parth Malwankar
faster mainline rev grep
55
def _linear_view_revisions(branch, start_rev_id, end_rev_id):
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
56
    # requires that start is older than end
0.40.95 by Parth Malwankar
faster mainline rev grep
57
    repo = branch.repository
58
    for revision_id in repo.iter_reverse_revision_history(end_rev_id):
59
        revno = branch.revision_id_to_dotted_revno(revision_id)
60
        revno_str = '.'.join(str(n) for n in revno)
61
        if revision_id == start_rev_id:
62
            yield revision_id, revno_str, 0
63
            break
64
        yield revision_id, revno_str, 0
65
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
66
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
67
# NOTE: _graph_view_revisions is copied from
68
# bzrlib.log._graph_view_revisions.
69
# This should probably be a common public API
70
def _graph_view_revisions(branch, start_rev_id, end_rev_id,
71
                          rebase_initial_depths=True):
72
    """Calculate revisions to view including merges, newest to oldest.
73
74
    :param branch: the branch
75
    :param start_rev_id: the lower revision-id
76
    :param end_rev_id: the upper revision-id
77
    :param rebase_initial_depth: should depths be rebased until a mainline
78
      revision is found?
79
    :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples.
80
    """
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
81
    # requires that start is older than end
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
82
    view_revisions = branch.iter_merge_sorted_revisions(
83
        start_revision_id=end_rev_id, stop_revision_id=start_rev_id,
84
        stop_rule="with-merges")
85
    if not rebase_initial_depths:
86
        for (rev_id, merge_depth, revno, end_of_merge
87
             ) in view_revisions:
88
            yield rev_id, '.'.join(map(str, revno)), merge_depth
89
    else:
90
        # We're following a development line starting at a merged revision.
91
        # We need to adjust depths down by the initial depth until we find
92
        # a depth less than it. Then we use that depth as the adjustment.
93
        # If and when we reach the mainline, depth adjustment ends.
94
        depth_adjustment = None
95
        for (rev_id, merge_depth, revno, end_of_merge
96
             ) in view_revisions:
97
            if depth_adjustment is None:
98
                depth_adjustment = merge_depth
99
            if depth_adjustment:
100
                if merge_depth < depth_adjustment:
101
                    # From now on we reduce the depth adjustement, this can be
102
                    # surprising for users. The alternative requires two passes
103
                    # which breaks the fast display of the first revision
104
                    # though.
105
                    depth_adjustment = merge_depth
106
                merge_depth -= depth_adjustment
107
            yield rev_id, '.'.join(map(str, revno)), merge_depth
108
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
109
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
110
def compile_pattern(pattern, flags=0):
111
    patternc = None
112
    try:
113
        # use python's re.compile as we need to catch re.error in case of bad pattern
114
        lazy_regex.reset_compile()
115
        patternc = re.compile(pattern, flags)
116
    except re.error, e:
117
        raise errors.BzrError("Invalid pattern: '%s'" % pattern)
118
    return patternc
119
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
120
0.40.86 by Parth Malwankar
the check for implicit fixed_string now allows for spaces.
121
def is_fixed_string(s):
0.40.101 by Parth Malwankar
added underscore to --fixed-string whitelist
122
    if re.match("^([A-Za-z0-9_]|\s)*$", s):
0.40.86 by Parth Malwankar
the check for implicit fixed_string now allows for spaces.
123
        return True
124
    return False
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
125
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
126
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
127
def versioned_grep(opts):
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
128
    wt, branch, relpath = \
129
        bzrdir.BzrDir.open_containing_tree_or_branch('.')
130
    branch.lock_read()
0.40.88 by Parth Malwankar
updated to avoid relocking.
131
    try:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
132
        start_rev = opts.revision[0]
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
133
        start_revid = start_rev.as_revision_id(branch)
0.40.95 by Parth Malwankar
faster mainline rev grep
134
        if start_revid == None:
135
            start_rev = RevisionSpec_revno.from_string("revno:1")
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
136
            start_revid = start_rev.as_revision_id(branch)
137
        srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
0.40.88 by Parth Malwankar
updated to avoid relocking.
138
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
139
        if len(opts.revision) == 2:
140
            end_rev = opts.revision[1]
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
141
            end_revid = end_rev.as_revision_id(branch)
0.40.95 by Parth Malwankar
faster mainline rev grep
142
            if end_revid == None:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
143
                end_revno, end_revid = branch.last_revision_info()
144
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
0.40.95 by Parth Malwankar
faster mainline rev grep
145
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
146
            grep_mainline = (_rev_on_mainline(srevno_tuple) and
147
                _rev_on_mainline(erevno_tuple))
148
149
            # ensure that we go in reverse order
150
            if srevno_tuple > erevno_tuple:
151
                srevno_tuple, erevno_tuple = erevno_tuple, srevno_tuple
152
                start_revid, end_revid = end_revid, start_revid
0.40.97 by Parth Malwankar
fixed caching bug for rev range.
153
0.40.95 by Parth Malwankar
faster mainline rev grep
154
            # Optimization: Traversing the mainline in reverse order is much
155
            # faster when we don't want to look at merged revs. We try this
156
            # with _linear_view_revisions. If all revs are to be grepped we
157
            # use the slower _graph_view_revisions
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
158
            if opts.levels==1 and grep_mainline:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
159
                given_revs = _linear_view_revisions(branch, start_revid, end_revid)
0.40.95 by Parth Malwankar
faster mainline rev grep
160
            else:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
161
                given_revs = _graph_view_revisions(branch, start_revid, end_revid)
0.40.88 by Parth Malwankar
updated to avoid relocking.
162
        else:
0.40.94 by Parth Malwankar
code cleanup. moved start_rev_tuple into if cond that uses it.
163
            # We do an optimization below. For grepping a specific revison
164
            # We don't need to call _graph_view_revisions which is slow.
165
            # We create the start_rev_tuple for only that specific revision.
166
            # _graph_view_revisions is used only for revision range.
167
            start_revno = '.'.join(map(str, srevno_tuple))
168
            start_rev_tuple = (start_revid, start_revno, 0)
0.40.88 by Parth Malwankar
updated to avoid relocking.
169
            given_revs = [start_rev_tuple]
170
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
171
        # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
172
        opts.outputter = _Outputter(opts, use_cache=True)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
173
0.40.88 by Parth Malwankar
updated to avoid relocking.
174
        for revid, revno, merge_depth in given_revs:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
175
            if opts.levels == 1 and merge_depth != 0:
0.40.88 by Parth Malwankar
updated to avoid relocking.
176
                # with level=1 show only top level
177
                continue
178
179
            rev = RevisionSpec_revid.from_string("revid:"+revid)
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
180
            tree = rev.as_tree(branch)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
181
            for path in opts.path_list:
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
182
                path_for_id = osutils.pathjoin(relpath, path)
183
                id = tree.path2id(path_for_id)
184
                if not id:
0.41.22 by Parth Malwankar
added basic --exclude/include tests
185
                    trace.warning("Skipped unknown file '%s'." % path)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
186
                    continue
187
188
                if osutils.isdir(path):
189
                    path_prefix = path
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
190
                    dir_grep(tree, path, relpath, opts, revno, path_prefix)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
191
                else:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
192
                    versioned_file_grep(tree, id, '.', path, opts, revno)
0.40.88 by Parth Malwankar
updated to avoid relocking.
193
    finally:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
194
        branch.unlock()
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
195
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
196
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
197
def workingtree_grep(opts):
198
    revno = opts.print_revno = None # for working tree set revno to None
0.40.69 by Parth Malwankar
reduced lock/unlock
199
200
    tree, branch, relpath = \
201
        bzrdir.BzrDir.open_containing_tree_or_branch('.')
0.40.130 by Parth Malwankar
grep in a branch with no tree does not throw stack trace (#572658)
202
    if not tree:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
203
        msg = ('Cannot search working tree. Working tree not found.\n'
204
            'To search for specific revision in history use the -r option.')
0.40.130 by Parth Malwankar
grep in a branch with no tree does not throw stack trace (#572658)
205
        raise errors.BzrCommandError(msg)
206
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
207
    # GZ 2010-06-02: Shouldn't be smuggling this on opts, but easy for now
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
208
    opts.outputter = _Outputter(opts)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
209
0.40.69 by Parth Malwankar
reduced lock/unlock
210
    tree.lock_read()
211
    try:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
212
        for path in opts.path_list:
0.40.69 by Parth Malwankar
reduced lock/unlock
213
            if osutils.isdir(path):
214
                path_prefix = path
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
215
                dir_grep(tree, path, relpath, opts, revno, path_prefix)
0.40.69 by Parth Malwankar
reduced lock/unlock
216
            else:
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
217
                _file_grep(open(path).read(), path, opts, revno)
0.40.69 by Parth Malwankar
reduced lock/unlock
218
    finally:
219
        tree.unlock()
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
220
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
221
0.40.74 by Parth Malwankar
optimization. --include/exclude are checked before reading the file.
222
def _skip_file(include, exclude, path):
223
    if include and not _path_in_glob_list(path, include):
224
        return True
225
    if exclude and _path_in_glob_list(path, exclude):
226
        return True
227
    return False
228
229
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
230
def dir_grep(tree, path, relpath, opts, revno, path_prefix):
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
231
    # setup relpath to open files relative to cwd
232
    rpath = relpath
233
    if relpath:
234
        rpath = osutils.pathjoin('..',relpath)
235
236
    from_dir = osutils.pathjoin(relpath, path)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
237
    if opts.from_root:
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
238
        # start searching recursively from root
239
        from_dir=None
240
        recursive=True
241
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
242
    to_grep = []
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
243
    to_grep_append = to_grep.append
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
244
    # GZ 2010-06-05: The cache dict used to be recycled every call to dir_grep
245
    #                and hits manually refilled. Could do this again if it was
246
    #                for a good reason, otherwise cache might want purging.
247
    outputter = opts.outputter
0.40.69 by Parth Malwankar
reduced lock/unlock
248
    for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
249
        from_dir=from_dir, recursive=opts.recursive):
0.40.69 by Parth Malwankar
reduced lock/unlock
250
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
251
        if _skip_file(opts.include, opts.exclude, fp):
0.40.74 by Parth Malwankar
optimization. --include/exclude are checked before reading the file.
252
            continue
253
0.40.69 by Parth Malwankar
reduced lock/unlock
254
        if fc == 'V' and fkind == 'file':
255
            if revno != None:
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
256
                # If old result is valid, print results immediately.
257
                # Otherwise, add file info to to_grep so that the
258
                # loop later will get chunks and grep them
0.46.11 by Martin
Add method to outputter for writing cached lines
259
                cache_id = tree.inventory[fid].revision
260
                if cache_id in outputter.cache:
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
261
                    # GZ 2010-06-05: Not really sure caching and re-outputting
262
                    #                the old path is really the right thing,
263
                    #                but it's what the old code seemed to do
0.46.11 by Martin
Add method to outputter for writing cached lines
264
                    outputter.write_cached_lines(cache_id, revno)
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
265
                else:
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
266
                    to_grep_append((fid, (fp, fid)))
0.40.69 by Parth Malwankar
reduced lock/unlock
267
            else:
268
                # we are grepping working tree.
269
                if from_dir == None:
270
                    from_dir = '.'
271
272
                path_for_file = osutils.pathjoin(tree.basedir, from_dir, fp)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
273
                if opts.files_with_matches or opts.files_without_match:
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
274
                    # Optimize for wtree list-only as we don't need to read the
275
                    # entire file
0.46.20 by Martin
Remove some unneeded imports
276
                    file = open(path_for_file, 'r', buffering=4096)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
277
                    _file_grep_list_only_wtree(file, fp, opts, path_prefix)
0.40.121 by Parth Malwankar
initial implementation of -L/--files-without-matches. no tests.
278
                else:
0.46.20 by Martin
Remove some unneeded imports
279
                    file_text = open(path_for_file, 'r').read()
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
280
                    _file_grep(file_text, fp, opts, revno, path_prefix)
0.40.43 by Parth Malwankar
moved cmd_grep._grep_dir to grep.dir_grep
281
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
282
    if revno != None: # grep versioned files
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
283
        for (path, fid), chunks in tree.iter_files_bytes(to_grep):
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
284
            path = _make_display_path(relpath, path)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
285
            _file_grep(chunks[0], path, opts, revno, path_prefix,
286
                tree.inventory[fid].revision)
0.40.43 by Parth Malwankar
moved cmd_grep._grep_dir to grep.dir_grep
287
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
288
0.41.8 by Parth Malwankar
code cleanup.
289
def _make_display_path(relpath, path):
290
    """Return path string relative to user cwd.
0.40.42 by Parth Malwankar
fix to make grep paths relative to cwd
291
0.41.8 by Parth Malwankar
code cleanup.
292
    Take tree's 'relpath' and user supplied 'path', and return path
293
    that can be displayed to the user.
294
    """
0.40.15 by Parth Malwankar
some fixes and test updates
295
    if relpath:
0.40.52 by Parth Malwankar
code cleanup and documentation
296
        # update path so to display it w.r.t cwd
297
        # handle windows slash separator
0.40.20 by Parth Malwankar
used path functions from bzrlib.osutils
298
        path = osutils.normpath(osutils.pathjoin(relpath, path))
0.40.22 by Parth Malwankar
fixed display path formatting on windows
299
        path = path.replace('\\', '/')
300
        path = path.replace(relpath + '/', '', 1)
0.41.8 by Parth Malwankar
code cleanup.
301
    return path
302
303
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
304
def versioned_file_grep(tree, id, relpath, path, opts, revno, path_prefix = None):
0.41.10 by Parth Malwankar
code cleanup. added comments. path adjustment is now done
305
    """Create a file object for the specified id and pass it on to _file_grep.
306
    """
307
308
    path = _make_display_path(relpath, path)
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
309
    file_text = tree.get_file_text(id)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
310
    _file_grep(file_text, path, opts, revno, path_prefix)
0.41.21 by Parth Malwankar
include/exclude working now. tests not added.
311
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
312
0.41.21 by Parth Malwankar
include/exclude working now. tests not added.
313
def _path_in_glob_list(path, glob_list):
314
    for glob in glob_list:
315
        if fnmatch(path, glob):
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
316
            return True
317
    return False
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
318
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
319
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
320
def _file_grep_list_only_wtree(file, path, opts, path_prefix=None):
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
321
    # test and skip binary files
322
    if '\x00' in file.read(1024):
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
323
        if opts.verbose:
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
324
            trace.warning("Binary file '%s' skipped." % path)
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
325
        return
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
326
327
    file.seek(0) # search from beginning
328
329
    found = False
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
330
    if opts.fixed_string:
331
        pattern = opts.pattern.encode(_user_encoding, 'replace')
0.46.1 by Martin
Make -Fi use regexps for re.IGNORECASE rather than double str.lower
332
        for line in file:
333
            if pattern in line:
334
                found = True
335
                break
0.40.121 by Parth Malwankar
initial implementation of -L/--files-without-matches. no tests.
336
    else: # not fixed_string
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
337
        for line in file:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
338
            if opts.patternc.search(line):
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
339
                found = True
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
340
                break
341
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
342
    if (opts.files_with_matches and found) or \
343
        (opts.files_without_match and not found):
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
344
        if path_prefix and path_prefix != '.':
345
            # user has passed a dir arg, show that as result prefix
346
            path = osutils.pathjoin(path_prefix, path)
0.46.18 by Martin
Fix another, previously existing issue with colour and match-only
347
        opts.outputter.get_writer(path, None, None)()
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
348
349
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
350
class _Outputter(object):
351
    """Precalculate formatting based on options given
352
353
    The idea here is to do this work only once per run, and finally return a
354
    function that will do the minimum amount possible for each match.
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
355
    """
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
356
    def __init__(self, opts, use_cache=False):
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
357
        self.outf = opts.outf
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
358
        if use_cache:
359
            # self.cache is used to cache results for dir grep based on fid.
360
            # If the fid is does not change between results, it means that
361
            # the result will be the same apart from revno. In such a case
362
            # we avoid getting file chunks from repo and grepping. The result
363
            # is just printed by replacing old revno with new one.
364
            self.cache = {}
365
        else:
366
            self.cache = None
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
367
        no_line = opts.files_with_matches or opts.files_without_match
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
368
369
        if opts.show_color:
370
            pat = opts.pattern.encode(_user_encoding, 'replace')
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
371
            if no_line:
372
                self.get_writer = self._get_writer_plain
373
            elif opts.fixed_string:
374
                self._old = pat
375
                self._new = color_string(pat, FG.BOLD_RED)
376
                self.get_writer = self._get_writer_fixed_highlighted
377
            else:
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
378
                flags = opts.patternc.flags
379
                self._sub = re.compile(pat.join(("((?:",")+)")), flags).sub
380
                self._highlight = color_string("\\1", FG.BOLD_RED)
381
                self.get_writer = self._get_writer_regexp_highlighted
382
            path_start = FG.MAGENTA
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
383
            path_end = FG.NONE
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
384
            sep = color_string(':', FG.BOLD_CYAN)
385
            rev_sep = color_string('~', FG.BOLD_YELLOW)
386
        else:
387
            self.get_writer = self._get_writer_plain
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
388
            path_start = path_end = ""
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
389
            sep = ":"
390
            rev_sep = "~"
391
392
        parts = [path_start, "%(path)s"]
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
393
        if opts.print_revno:
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
394
            parts.extend([rev_sep, "%(revno)s"])
0.46.13 by Martin
Split format string into two parts for non-cached operations too
395
        self._format_initial = "".join(parts)
396
        parts = []
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
397
        if no_line:
398
            if not opts.print_revno:
399
                parts.append(path_end)
400
        else:
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
401
            if opts.line_number:
0.46.13 by Martin
Split format string into two parts for non-cached operations too
402
                parts.extend([sep, "%(lineno)s"])
403
            parts.extend([sep, "%(line)s"])
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
404
        parts.append(opts.eol_marker)
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
405
        self._format_perline = "".join(parts)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
406
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
407
    def _get_writer_plain(self, path, revno, cache_id):
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
408
        """Get function for writing uncoloured output"""
0.46.13 by Martin
Split format string into two parts for non-cached operations too
409
        per_line = self._format_perline
410
        start = self._format_initial % {"path":path, "revno":revno}
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
411
        write = self.outf.write
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
412
        if self.cache is not None and cache_id is not None:
413
            result_list = []
414
            self.cache[cache_id] = path, result_list
415
            add_to_cache = result_list.append
416
            def _line_cache_and_writer(**kwargs):
417
                """Write formatted line and cache arguments"""
0.46.12 by Martin
Split format string for cache to only store a string, not a dict
418
                end = per_line % kwargs
419
                add_to_cache(end)
420
                write(start + end)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
421
            return _line_cache_and_writer
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
422
        def _line_writer(**kwargs):
423
            """Write formatted line from arguments given by underlying opts"""
0.46.13 by Martin
Split format string into two parts for non-cached operations too
424
            write(start + per_line % kwargs)
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
425
        return _line_writer
426
0.46.11 by Martin
Add method to outputter for writing cached lines
427
    def write_cached_lines(self, cache_id, revno):
428
        """Write cached results out again for new revision"""
429
        cached_path, cached_matches = self.cache[cache_id]
0.46.12 by Martin
Split format string for cache to only store a string, not a dict
430
        start = self._format_initial % {"path":cached_path, "revno":revno}
0.46.11 by Martin
Add method to outputter for writing cached lines
431
        write = self.outf.write
0.46.12 by Martin
Split format string for cache to only store a string, not a dict
432
        for end in cached_matches:
433
            write(start + end)
0.46.11 by Martin
Add method to outputter for writing cached lines
434
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
435
    def _get_writer_regexp_highlighted(self, path, revno, cache_id):
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
436
        """Get function for writing output with regexp match highlighted"""
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
437
        _line_writer = self._get_writer_plain(path, revno, cache_id)
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
438
        sub, highlight = self._sub, self._highlight
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
439
        def _line_writer_regexp_highlighted(line, **kwargs):
440
            """Write formatted line with matched pattern highlighted"""
441
            return _line_writer(line=sub(highlight, line), **kwargs)
442
        return _line_writer_regexp_highlighted
443
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
444
    def _get_writer_fixed_highlighted(self, path, revno, cache_id):
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
445
        """Get function for writing output with search string highlighted"""
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
446
        _line_writer = self._get_writer_plain(path, revno, cache_id)
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
447
        old, new = self._old, self._new
448
        def _line_writer_fixed_highlighted(line, **kwargs):
449
            """Write formatted line with string searched for highlighted"""
450
            return _line_writer(line=line.replace(old, new), **kwargs)
451
        return _line_writer_fixed_highlighted
0.46.3 by Martin
Start moving formatting setup out of _file_grep, only for files_with_matches so far
452
453
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
454
def _file_grep(file_text, path, opts, revno, path_prefix=None, cache_id=None):
0.41.9 by Parth Malwankar
refactored code towards support for working tree grep.
455
    # test and skip binary files
0.40.62 by Parth Malwankar
performance optimization
456
    if '\x00' in file_text[:1024]:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
457
        if opts.verbose:
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
458
            trace.warning("Binary file '%s' skipped." % path)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
459
        return
0.41.9 by Parth Malwankar
refactored code towards support for working tree grep.
460
0.40.52 by Parth Malwankar
code cleanup and documentation
461
    if path_prefix and path_prefix != '.':
462
        # user has passed a dir arg, show that as result prefix
463
        path = osutils.pathjoin(path_prefix, path)
464
0.46.21 by Martin
Fix and test bytes/unicode issue but there's more to do in this area
465
    # GZ 2010-06-07: There's no actual guarentee the file contents will be in
466
    #                the user encoding, but we have to guess something and it
467
    #                is a reasonable default without a better mechanism.
468
    file_encoding = _user_encoding
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
469
    pattern = opts.pattern.encode(_user_encoding, 'replace')
0.43.8 by Parth Malwankar
added color for regex pattern.
470
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
471
    writeline = opts.outputter.get_writer(path, revno, cache_id)
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
472
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
473
    if opts.files_with_matches or opts.files_without_match:
0.40.112 by Parth Malwankar
support for -l, --files-with-matches. no tests yet.
474
        # While printing files with matches we only have two case
475
        # print file name or print file name with revno.
0.40.121 by Parth Malwankar
initial implementation of -L/--files-without-matches. no tests.
476
        found = False
0.46.2 by Martin
Remove redundant code on files_with_matches path in _file_grep
477
        if opts.fixed_string:
478
            for line in file_text.splitlines():
479
                if pattern in line:
480
                    found = True
481
                    break
0.40.112 by Parth Malwankar
support for -l, --files-with-matches. no tests yet.
482
        else:
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
483
            search = opts.patternc.search
0.46.2 by Martin
Remove redundant code on files_with_matches path in _file_grep
484
            for line in file_text.splitlines():
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
485
                if search(line):
0.46.2 by Martin
Remove redundant code on files_with_matches path in _file_grep
486
                    found = True
487
                    break
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
488
        if (opts.files_with_matches and found) or \
489
                (opts.files_without_match and not found):
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
490
            writeline()
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
491
    elif opts.fixed_string:
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
492
        if opts.line_number:
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
493
            for index, line in enumerate(file_text.splitlines()):
494
                if pattern in line:
0.46.21 by Martin
Fix and test bytes/unicode issue but there's more to do in this area
495
                    line = line.decode(file_encoding)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
496
                    writeline(lineno=index+1, line=line)
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
497
        else:
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
498
            for line in file_text.splitlines():
499
                if pattern in line:
0.46.21 by Martin
Fix and test bytes/unicode issue but there's more to do in this area
500
                    line = line.decode(file_encoding)
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
501
                    writeline(line=line)
0.40.63 by Parth Malwankar
performance: moved conditionals out of core loop.
502
    else:
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
503
        search = opts.patternc.search
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
504
        if opts.line_number:
505
            for index, line in enumerate(file_text.splitlines()):
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
506
                if search(line):
0.46.21 by Martin
Fix and test bytes/unicode issue but there's more to do in this area
507
                    line = line.decode(file_encoding)
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
508
                    writeline(lineno=index+1, line=line)
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
509
        else:
510
            for line in file_text.splitlines():
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
511
                if search(line):
0.46.21 by Martin
Fix and test bytes/unicode issue but there's more to do in this area
512
                    line = line.decode(file_encoding)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
513
                    writeline(line=line)