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