/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
7211.14.6 by Jelmer Vernooij
Fix source tests.
2
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
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
7211.14.6 by Jelmer Vernooij
Fix source tests.
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
16
7479.2.1 by Jelmer Vernooij
Drop python2 support.
17
from io import BytesIO
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
18
import re
0.47.1 by Martin
Implement whole text search for fast failure on no match
19
7211.14.5 by Jelmer Vernooij
Fix tests.
20
from .lazy_import import lazy_import
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
21
lazy_import(globals(), """
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
22
from fnmatch import fnmatch
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
23
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
24
from breezy._termcolor import color_string, FG
0.43.4 by Parth Malwankar
initial support for color for fixed string grep.
25
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
26
from breezy import (
7211.14.2 by Jelmer Vernooij
Move GrepOptions to breezy.grep.
27
    diff,
28
    )
29
""")
30
from . import (
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
31
    controldir,
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
32
    errors,
7211.14.2 by Jelmer Vernooij
Move GrepOptions to breezy.grep.
33
    osutils,
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
34
    revision as _mod_revision,
0.40.47 by Parth Malwankar
fixes bug #531336. binary files are now skipped.
35
    trace,
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
36
    )
7211.14.2 by Jelmer Vernooij
Move GrepOptions to breezy.grep.
37
from .revisionspec import (
6800.1.5 by Jelmer Vernooij
Fix more imports.
38
    RevisionSpec,
39
    RevisionSpec_revid,
40
    RevisionSpec_revno,
41
    )
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
42
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
43
_user_encoding = osutils.get_user_encoding()
44
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
45
0.40.95 by Parth Malwankar
faster mainline rev grep
46
class _RevisionNotLinear(Exception):
47
    """Raised when a revision is not on left-hand history."""
48
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
49
7211.14.2 by Jelmer Vernooij
Move GrepOptions to breezy.grep.
50
class GrepOptions(object):
51
    """Container to pass around grep options.
52
53
    This class is used as a container to pass around user option and
54
    some other params (like outf) to processing functions. This makes
55
    it easier to add more options as grep evolves.
56
    """
57
    verbose = False
58
    ignore_case = False
59
    no_recursive = False
60
    from_root = False
61
    null = False
62
    levels = None
63
    line_number = False
64
    path_list = None
65
    revision = None
66
    pattern = None
67
    include = None
68
    exclude = None
69
    fixed_string = False
70
    files_with_matches = False
71
    files_without_match = False
72
    color = None
73
    diff = False
74
75
    # derived options
76
    recursive = None
77
    eol_marker = None
78
    patternc = None
79
    sub_patternc = None
80
    print_revno = None
81
    fixed_string = None
82
    outf = None
83
    show_color = False
84
85
0.40.95 by Parth Malwankar
faster mainline rev grep
86
def _rev_on_mainline(rev_tuple):
87
    """returns True is rev tuple is on mainline"""
88
    if len(rev_tuple) == 1:
89
        return True
90
    return rev_tuple[1] == 0 and rev_tuple[2] == 0
91
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
92
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
93
# NOTE: _linear_view_revisions is basided on
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
94
# breezy.log._linear_view_revisions.
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
95
# This should probably be a common public API
0.40.95 by Parth Malwankar
faster mainline rev grep
96
def _linear_view_revisions(branch, start_rev_id, end_rev_id):
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
97
    # requires that start is older than end
0.40.95 by Parth Malwankar
faster mainline rev grep
98
    repo = branch.repository
6531.3.6 by Jelmer Vernooij
Use iter_lefthand_ancestry rather than removed iter_reverse_revision_history.
99
    graph = repo.get_graph()
6531.3.7 by Jelmer Vernooij
Formatting.
100
    for revision_id in graph.iter_lefthand_ancestry(
101
            end_rev_id, (_mod_revision.NULL_REVISION, )):
0.40.95 by Parth Malwankar
faster mainline rev grep
102
        revno = branch.revision_id_to_dotted_revno(revision_id)
103
        revno_str = '.'.join(str(n) for n in revno)
104
        if revision_id == start_rev_id:
105
            yield revision_id, revno_str, 0
106
            break
107
        yield revision_id, revno_str, 0
108
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
109
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
110
# NOTE: _graph_view_revisions is copied from
6622.1.34 by Jelmer Vernooij
Rename brzlib => breezy.
111
# breezy.log._graph_view_revisions.
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
112
# This should probably be a common public API
113
def _graph_view_revisions(branch, start_rev_id, end_rev_id,
114
                          rebase_initial_depths=True):
115
    """Calculate revisions to view including merges, newest to oldest.
116
117
    :param branch: the branch
118
    :param start_rev_id: the lower revision-id
119
    :param end_rev_id: the upper revision-id
120
    :param rebase_initial_depth: should depths be rebased until a mainline
121
      revision is found?
122
    :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples.
123
    """
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
124
    # requires that start is older than end
0.40.100 by Parth Malwankar
removed dependency on log._graph_view_revisions
125
    view_revisions = branch.iter_merge_sorted_revisions(
126
        start_revision_id=end_rev_id, stop_revision_id=start_rev_id,
127
        stop_rule="with-merges")
128
    if not rebase_initial_depths:
129
        for (rev_id, merge_depth, revno, end_of_merge
130
             ) in view_revisions:
131
            yield rev_id, '.'.join(map(str, revno)), merge_depth
132
    else:
133
        # We're following a development line starting at a merged revision.
134
        # We need to adjust depths down by the initial depth until we find
135
        # a depth less than it. Then we use that depth as the adjustment.
136
        # If and when we reach the mainline, depth adjustment ends.
137
        depth_adjustment = None
138
        for (rev_id, merge_depth, revno, end_of_merge
139
             ) in view_revisions:
140
            if depth_adjustment is None:
141
                depth_adjustment = merge_depth
142
            if depth_adjustment:
143
                if merge_depth < depth_adjustment:
144
                    # From now on we reduce the depth adjustement, this can be
145
                    # surprising for users. The alternative requires two passes
146
                    # which breaks the fast display of the first revision
147
                    # though.
148
                    depth_adjustment = merge_depth
149
                merge_depth -= depth_adjustment
150
            yield rev_id, '.'.join(map(str, revno)), merge_depth
151
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
152
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
153
def compile_pattern(pattern, flags=0):
154
    try:
7169.2.2 by Martin
Fix bp.grep test
155
        return re.compile(pattern, flags)
6619.3.2 by Jelmer Vernooij
Apply 2to3 except fix.
156
    except re.error as e:
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
157
        raise errors.BzrError("Invalid pattern: '%s'" % pattern)
7169.2.2 by Martin
Fix bp.grep test
158
    return None
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
159
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
160
0.40.86 by Parth Malwankar
the check for implicit fixed_string now allows for spaces.
161
def is_fixed_string(s):
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
162
    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.
163
        return True
164
    return False
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
165
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
166
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
167
class _GrepDiffOutputter(object):
168
    """Precalculate formatting based on options given for diff grep.
169
    """
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
170
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
171
    def __init__(self, opts):
0.48.8 by Parth Malwankar
colored header for diff grep output
172
        self.opts = opts
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
173
        self.outf = opts.outf
174
        if opts.show_color:
175
            if opts.fixed_string:
7027.9.1 by Jelmer Vernooij
Fix all but one remaining grep tests.
176
                self._old = opts.pattern
177
                self._new = color_string(opts.pattern, FG.BOLD_RED)
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
178
                self.get_writer = self._get_writer_fixed_highlighted
179
            else:
180
                flags = opts.patternc.flags
7143.15.2 by Jelmer Vernooij
Run autopep8.
181
                self._sub = re.compile(
182
                    opts.pattern.join(("((?:", ")+)")), flags).sub
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
183
                self._highlight = color_string("\\1", FG.BOLD_RED)
184
                self.get_writer = self._get_writer_regexp_highlighted
185
        else:
186
            self.get_writer = self._get_writer_plain
187
0.48.8 by Parth Malwankar
colored header for diff grep output
188
    def get_file_header_writer(self):
189
        """Get function for writing file headers"""
190
        write = self.outf.write
191
        eol_marker = self.opts.eol_marker
7143.15.2 by Jelmer Vernooij
Run autopep8.
192
0.48.8 by Parth Malwankar
colored header for diff grep output
193
        def _line_writer(line):
194
            write(line + eol_marker)
7143.15.2 by Jelmer Vernooij
Run autopep8.
195
0.48.8 by Parth Malwankar
colored header for diff grep output
196
        def _line_writer_color(line):
197
            write(FG.BOLD_MAGENTA + line + FG.NONE + eol_marker)
198
        if self.opts.show_color:
199
            return _line_writer_color
200
        else:
201
            return _line_writer
202
        return _line_writer
203
204
    def get_revision_header_writer(self):
205
        """Get function for writing revno lines"""
206
        write = self.outf.write
207
        eol_marker = self.opts.eol_marker
7143.15.2 by Jelmer Vernooij
Run autopep8.
208
0.48.8 by Parth Malwankar
colored header for diff grep output
209
        def _line_writer(line):
210
            write(line + eol_marker)
7143.15.2 by Jelmer Vernooij
Run autopep8.
211
0.48.8 by Parth Malwankar
colored header for diff grep output
212
        def _line_writer_color(line):
213
            write(FG.BOLD_BLUE + line + FG.NONE + eol_marker)
214
        if self.opts.show_color:
215
            return _line_writer_color
216
        else:
217
            return _line_writer
218
        return _line_writer
219
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
220
    def _get_writer_plain(self):
221
        """Get function for writing uncoloured output"""
222
        write = self.outf.write
0.48.8 by Parth Malwankar
colored header for diff grep output
223
        eol_marker = self.opts.eol_marker
7143.15.2 by Jelmer Vernooij
Run autopep8.
224
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
225
        def _line_writer(line):
0.48.8 by Parth Malwankar
colored header for diff grep output
226
            write(line + eol_marker)
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
227
        return _line_writer
228
229
    def _get_writer_regexp_highlighted(self):
230
        """Get function for writing output with regexp match highlighted"""
231
        _line_writer = self._get_writer_plain()
232
        sub, highlight = self._sub, self._highlight
7143.15.2 by Jelmer Vernooij
Run autopep8.
233
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
234
        def _line_writer_regexp_highlighted(line):
235
            """Write formatted line with matched pattern highlighted"""
236
            return _line_writer(line=sub(highlight, line))
237
        return _line_writer_regexp_highlighted
238
239
    def _get_writer_fixed_highlighted(self):
240
        """Get function for writing output with search string highlighted"""
241
        _line_writer = self._get_writer_plain()
242
        old, new = self._old, self._new
7143.15.2 by Jelmer Vernooij
Run autopep8.
243
0.48.7 by Parth Malwankar
initial outputter support for diff_grep
244
        def _line_writer_fixed_highlighted(line):
245
            """Write formatted line with string searched for highlighted"""
246
            return _line_writer(line=line.replace(old, new))
247
        return _line_writer_fixed_highlighted
248
249
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
250
def grep_diff(opts):
251
    wt, branch, relpath = \
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
252
        controldir.ControlDir.open_containing_tree_or_branch('.')
6754.8.4 by Jelmer Vernooij
Use new context stuff.
253
    with branch.lock_read():
0.48.3 by Parth Malwankar
for grep_diff, if rev is not specified, last is used as start.
254
        if opts.revision:
255
            start_rev = opts.revision[0]
256
        else:
0.48.9 by Parth Malwankar
added inital test for 'grep -p'
257
            # if no revision is sepcified for diff grep we grep all changesets.
258
            opts.revision = [RevisionSpec.from_string('revno:1'),
7143.15.2 by Jelmer Vernooij
Run autopep8.
259
                             RevisionSpec.from_string('last:1')]
0.48.3 by Parth Malwankar
for grep_diff, if rev is not specified, last is used as start.
260
            start_rev = opts.revision[0]
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
261
        start_revid = start_rev.as_revision_id(branch)
6973.14.6 by Jelmer Vernooij
Fix some more tests.
262
        if start_revid == b'null:':
0.48.4 by Parth Malwankar
diff grep now works.
263
            return
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
264
        srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
265
        if len(opts.revision) == 2:
266
            end_rev = opts.revision[1]
267
            end_revid = end_rev.as_revision_id(branch)
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
268
            if end_revid is None:
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
269
                end_revno, end_revid = branch.last_revision_info()
270
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
271
7143.15.2 by Jelmer Vernooij
Run autopep8.
272
            grep_mainline = (_rev_on_mainline(srevno_tuple)
273
                             and _rev_on_mainline(erevno_tuple))
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
274
275
            # ensure that we go in reverse order
276
            if srevno_tuple > erevno_tuple:
277
                srevno_tuple, erevno_tuple = erevno_tuple, srevno_tuple
278
                start_revid, end_revid = end_revid, start_revid
279
280
            # Optimization: Traversing the mainline in reverse order is much
281
            # faster when we don't want to look at merged revs. We try this
282
            # with _linear_view_revisions. If all revs are to be grepped we
283
            # use the slower _graph_view_revisions
7143.15.2 by Jelmer Vernooij
Run autopep8.
284
            if opts.levels == 1 and grep_mainline:
285
                given_revs = _linear_view_revisions(
286
                    branch, start_revid, end_revid)
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
287
            else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
288
                given_revs = _graph_view_revisions(
289
                    branch, start_revid, end_revid)
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
290
        else:
291
            # We do an optimization below. For grepping a specific revison
292
            # We don't need to call _graph_view_revisions which is slow.
293
            # We create the start_rev_tuple for only that specific revision.
294
            # _graph_view_revisions is used only for revision range.
295
            start_revno = '.'.join(map(str, srevno_tuple))
296
            start_rev_tuple = (start_revid, start_revno, 0)
297
            given_revs = [start_rev_tuple]
298
        repo = branch.repository
7143.15.2 by Jelmer Vernooij
Run autopep8.
299
        diff_pattern = re.compile(
300
            b"^[+\\-].*(" + opts.pattern.encode(_user_encoding) + b")")
7027.9.1 by Jelmer Vernooij
Fix all but one remaining grep tests.
301
        file_pattern = re.compile(b"=== (modified|added|removed) file '.*'")
0.48.8 by Parth Malwankar
colored header for diff grep output
302
        outputter = _GrepDiffOutputter(opts)
303
        writeline = outputter.get_writer()
304
        writerevno = outputter.get_revision_header_writer()
305
        writefileheader = outputter.get_file_header_writer()
0.48.11 by Parth Malwankar
unicode decode fix for diff grep.
306
        file_encoding = _user_encoding
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
307
        for revid, revno, merge_depth in given_revs:
308
            if opts.levels == 1 and merge_depth != 0:
309
                # with level=1 show only top level
310
                continue
311
7143.15.2 by Jelmer Vernooij
Run autopep8.
312
            rev_spec = RevisionSpec_revid.from_string(
313
                "revid:" + revid.decode('utf-8'))
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
314
            new_rev = repo.get_revision(revid)
315
            new_tree = rev_spec.as_tree(branch)
316
            if len(new_rev.parent_ids) == 0:
317
                ancestor_id = _mod_revision.NULL_REVISION
318
            else:
319
                ancestor_id = new_rev.parent_ids[0]
320
            old_tree = repo.revision_tree(ancestor_id)
6621.22.2 by Martin
Use BytesIO or StringIO from bzrlib.sixish
321
            s = BytesIO()
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
322
            diff.show_diff_trees(old_tree, new_tree, s,
7143.15.2 by Jelmer Vernooij
Run autopep8.
323
                                 old_label='', new_label='')
0.48.4 by Parth Malwankar
diff grep now works.
324
            display_revno = True
325
            display_file = False
326
            file_header = None
327
            text = s.getvalue()
0.48.6 by Parth Malwankar
removed fixed_string condition for diff grep
328
            for line in text.splitlines():
329
                if file_pattern.search(line):
330
                    file_header = line
331
                    display_file = True
0.48.11 by Parth Malwankar
unicode decode fix for diff grep.
332
                elif diff_pattern.search(line):
0.48.6 by Parth Malwankar
removed fixed_string condition for diff grep
333
                    if display_revno:
0.48.10 by Parth Malwankar
more tests for 'grep --diff'
334
                        writerevno("=== revno:%s ===" % (revno,))
0.48.6 by Parth Malwankar
removed fixed_string condition for diff grep
335
                        display_revno = False
336
                    if display_file:
7143.15.2 by Jelmer Vernooij
Run autopep8.
337
                        writefileheader(
338
                            "  %s" % (file_header.decode(file_encoding, 'replace'),))
0.48.6 by Parth Malwankar
removed fixed_string condition for diff grep
339
                        display_file = False
0.48.11 by Parth Malwankar
unicode decode fix for diff grep.
340
                    line = line.decode(file_encoding, 'replace')
341
                    writeline("    %s" % (line,))
0.48.2 by Parth Malwankar
intermediate checkin. we now show diff with -p option.
342
343
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
344
def versioned_grep(opts):
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
345
    wt, branch, relpath = \
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
346
        controldir.ControlDir.open_containing_tree_or_branch('.')
6754.8.4 by Jelmer Vernooij
Use new context stuff.
347
    with branch.lock_read():
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
348
        start_rev = opts.revision[0]
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
349
        start_revid = start_rev.as_revision_id(branch)
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
350
        if start_revid is None:
0.40.95 by Parth Malwankar
faster mainline rev grep
351
            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.
352
            start_revid = start_rev.as_revision_id(branch)
353
        srevno_tuple = branch.revision_id_to_dotted_revno(start_revid)
0.40.88 by Parth Malwankar
updated to avoid relocking.
354
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
355
        if len(opts.revision) == 2:
356
            end_rev = opts.revision[1]
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
357
            end_revid = end_rev.as_revision_id(branch)
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
358
            if end_revid is None:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
359
                end_revno, end_revid = branch.last_revision_info()
360
            erevno_tuple = branch.revision_id_to_dotted_revno(end_revid)
0.40.95 by Parth Malwankar
faster mainline rev grep
361
7143.15.2 by Jelmer Vernooij
Run autopep8.
362
            grep_mainline = (_rev_on_mainline(srevno_tuple)
363
                             and _rev_on_mainline(erevno_tuple))
0.40.106 by Parth Malwankar
fixed error in dotted rev reverse search.
364
365
            # ensure that we go in reverse order
366
            if srevno_tuple > erevno_tuple:
367
                srevno_tuple, erevno_tuple = erevno_tuple, srevno_tuple
368
                start_revid, end_revid = end_revid, start_revid
0.40.97 by Parth Malwankar
fixed caching bug for rev range.
369
0.40.95 by Parth Malwankar
faster mainline rev grep
370
            # Optimization: Traversing the mainline in reverse order is much
371
            # faster when we don't want to look at merged revs. We try this
372
            # with _linear_view_revisions. If all revs are to be grepped we
373
            # use the slower _graph_view_revisions
6531.3.9 by Jelmer Vernooij
Remove broken tests..
374
            if opts.levels == 1 and grep_mainline:
7143.15.2 by Jelmer Vernooij
Run autopep8.
375
                given_revs = _linear_view_revisions(
376
                    branch, start_revid, end_revid)
0.40.95 by Parth Malwankar
faster mainline rev grep
377
            else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
378
                given_revs = _graph_view_revisions(
379
                    branch, start_revid, end_revid)
0.40.88 by Parth Malwankar
updated to avoid relocking.
380
        else:
0.40.94 by Parth Malwankar
code cleanup. moved start_rev_tuple into if cond that uses it.
381
            # We do an optimization below. For grepping a specific revison
382
            # We don't need to call _graph_view_revisions which is slow.
383
            # We create the start_rev_tuple for only that specific revision.
384
            # _graph_view_revisions is used only for revision range.
385
            start_revno = '.'.join(map(str, srevno_tuple))
386
            start_rev_tuple = (start_revid, start_revno, 0)
0.40.88 by Parth Malwankar
updated to avoid relocking.
387
            given_revs = [start_rev_tuple]
388
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
389
        # 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
390
        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
391
0.40.88 by Parth Malwankar
updated to avoid relocking.
392
        for revid, revno, merge_depth in given_revs:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
393
            if opts.levels == 1 and merge_depth != 0:
0.40.88 by Parth Malwankar
updated to avoid relocking.
394
                # with level=1 show only top level
395
                continue
396
7143.15.2 by Jelmer Vernooij
Run autopep8.
397
            rev = RevisionSpec_revid.from_string(
398
                "revid:" + revid.decode('utf-8'))
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
399
            tree = rev.as_tree(branch)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
400
            for path in opts.path_list:
6874.2.5 by Jelmer Vernooij
Fix grep.
401
                tree_path = osutils.pathjoin(relpath, path)
402
                if not tree.has_filename(tree_path):
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
403
                    trace.warning("Skipped unknown file '%s'.", path)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
404
                    continue
405
406
                if osutils.isdir(path):
407
                    path_prefix = path
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
408
                    dir_grep(tree, path, relpath, opts, revno, path_prefix)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
409
                else:
7143.15.2 by Jelmer Vernooij
Run autopep8.
410
                    versioned_file_grep(
411
                        tree, tree_path, '.', path, opts, revno)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
412
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
413
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
414
def workingtree_grep(opts):
7143.15.2 by Jelmer Vernooij
Run autopep8.
415
    revno = opts.print_revno = None  # for working tree set revno to None
0.40.69 by Parth Malwankar
reduced lock/unlock
416
417
    tree, branch, relpath = \
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
418
        controldir.ControlDir.open_containing_tree_or_branch('.')
0.40.130 by Parth Malwankar
grep in a branch with no tree does not throw stack trace (#572658)
419
    if not tree:
0.40.131 by Parth Malwankar
bzr grep now allows grepping with -r even when no tree exists.
420
        msg = ('Cannot search working tree. Working tree not found.\n'
7143.15.2 by Jelmer Vernooij
Run autopep8.
421
               'To search for specific revision in history use the -r option.')
7490.61.1 by Jelmer Vernooij
Rename BzrCommandError to CommandError.
422
        raise errors.CommandError(msg)
0.40.130 by Parth Malwankar
grep in a branch with no tree does not throw stack trace (#572658)
423
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
424
    # 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
425
    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
426
6754.8.4 by Jelmer Vernooij
Use new context stuff.
427
    with tree.lock_read():
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
428
        for path in opts.path_list:
0.40.69 by Parth Malwankar
reduced lock/unlock
429
            if osutils.isdir(path):
430
                path_prefix = path
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
431
                dir_grep(tree, path, relpath, opts, revno, path_prefix)
0.40.69 by Parth Malwankar
reduced lock/unlock
432
            else:
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
433
                with open(path, 'rb') as f:
434
                    _file_grep(f.read(), path, opts, revno)
0.41.11 by Parth Malwankar
moved top level grep code to versioned_grep.
435
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
436
0.40.74 by Parth Malwankar
optimization. --include/exclude are checked before reading the file.
437
def _skip_file(include, exclude, path):
438
    if include and not _path_in_glob_list(path, include):
439
        return True
440
    if exclude and _path_in_glob_list(path, exclude):
441
        return True
442
    return False
443
444
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
445
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
446
    # setup relpath to open files relative to cwd
447
    rpath = relpath
448
    if relpath:
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
449
        rpath = osutils.pathjoin('..', relpath)
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
450
451
    from_dir = osutils.pathjoin(relpath, path)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
452
    if opts.from_root:
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
453
        # start searching recursively from root
6874.2.1 by Jelmer Vernooij
Make Tree.iter_files_bytes() take paths rather than file_ids.
454
        from_dir = None
455
        recursive = True
0.40.60 by Parth Malwankar
'binary file skipped' warning is only shown with --verbose flag
456
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
457
    to_grep = []
0.40.92 by Parth Malwankar
performance tweaks to core cached result print loop.
458
    to_grep_append = to_grep.append
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
459
    # GZ 2010-06-05: The cache dict used to be recycled every call to dir_grep
460
    #                and hits manually refilled. Could do this again if it was
461
    #                for a good reason, otherwise cache might want purging.
462
    outputter = opts.outputter
7143.19.7 by Jelmer Vernooij
merge trunk
463
    for fp, fc, fkind, entry in tree.list_files(
464
            include_root=False, from_dir=from_dir, recursive=opts.recursive):
0.40.69 by Parth Malwankar
reduced lock/unlock
465
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
466
        if _skip_file(opts.include, opts.exclude, fp):
0.40.74 by Parth Malwankar
optimization. --include/exclude are checked before reading the file.
467
            continue
468
7143.19.5 by Jelmer Vernooij
Undo removal of kind.
469
        if fc == 'V' and fkind == 'file':
6928.1.1 by Jelmer Vernooij
Pass in correct paths in grep.
470
            tree_path = osutils.pathjoin(from_dir if from_dir else '', fp)
471
            if revno is not None:
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
472
                # If old result is valid, print results immediately.
473
                # Otherwise, add file info to to_grep so that the
474
                # loop later will get chunks and grep them
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
475
                cache_id = tree.get_file_revision(tree_path)
0.46.11 by Martin
Add method to outputter for writing cached lines
476
                if cache_id in outputter.cache:
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
477
                    # GZ 2010-06-05: Not really sure caching and re-outputting
478
                    #                the old path is really the right thing,
479
                    #                but it's what the old code seemed to do
0.46.11 by Martin
Add method to outputter for writing cached lines
480
                    outputter.write_cached_lines(cache_id, revno)
0.40.90 by Parth Malwankar
significant speedup for revision range grep by caching old result.
481
                else:
6928.1.1 by Jelmer Vernooij
Pass in correct paths in grep.
482
                    to_grep_append((tree_path, (fp, tree_path)))
0.40.69 by Parth Malwankar
reduced lock/unlock
483
            else:
484
                # we are grepping working tree.
6531.3.8 by Jelmer Vernooij
Move color feature into bzrlib.tests.features.
485
                if from_dir is None:
0.40.69 by Parth Malwankar
reduced lock/unlock
486
                    from_dir = '.'
487
488
                path_for_file = osutils.pathjoin(tree.basedir, from_dir, fp)
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
489
                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.
490
                    # Optimize for wtree list-only as we don't need to read the
491
                    # entire file
7027.9.1 by Jelmer Vernooij
Fix all but one remaining grep tests.
492
                    with open(path_for_file, 'rb', buffering=4096) as file:
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
493
                        _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.
494
                else:
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
495
                    with open(path_for_file, 'rb') as f:
496
                        _file_grep(f.read(), fp, opts, revno, path_prefix)
0.40.43 by Parth Malwankar
moved cmd_grep._grep_dir to grep.dir_grep
497
7143.15.2 by Jelmer Vernooij
Run autopep8.
498
    if revno is not None:  # grep versioned files
6928.1.1 by Jelmer Vernooij
Pass in correct paths in grep.
499
        for (path, tree_path), chunks in tree.iter_files_bytes(to_grep):
0.40.85 by Parth Malwankar
optimized versioned grep to use iter_files_bytes.
500
            path = _make_display_path(relpath, path)
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
501
            _file_grep(b''.join(chunks), path, opts, revno, path_prefix,
7143.15.2 by Jelmer Vernooij
Run autopep8.
502
                       tree.get_file_revision(tree_path))
0.40.43 by Parth Malwankar
moved cmd_grep._grep_dir to grep.dir_grep
503
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
504
0.41.8 by Parth Malwankar
code cleanup.
505
def _make_display_path(relpath, path):
506
    """Return path string relative to user cwd.
0.40.42 by Parth Malwankar
fix to make grep paths relative to cwd
507
0.41.8 by Parth Malwankar
code cleanup.
508
    Take tree's 'relpath' and user supplied 'path', and return path
509
    that can be displayed to the user.
510
    """
0.40.15 by Parth Malwankar
some fixes and test updates
511
    if relpath:
0.40.52 by Parth Malwankar
code cleanup and documentation
512
        # update path so to display it w.r.t cwd
513
        # handle windows slash separator
0.40.20 by Parth Malwankar
used path functions from bzrlib.osutils
514
        path = osutils.normpath(osutils.pathjoin(relpath, path))
0.40.22 by Parth Malwankar
fixed display path formatting on windows
515
        path = path.replace('\\', '/')
516
        path = path.replace(relpath + '/', '', 1)
0.41.8 by Parth Malwankar
code cleanup.
517
    return path
518
519
7143.15.2 by Jelmer Vernooij
Run autopep8.
520
def versioned_file_grep(tree, tree_path, relpath, path, opts, revno, path_prefix=None):
0.41.10 by Parth Malwankar
code cleanup. added comments. path adjustment is now done
521
    """Create a file object for the specified id and pass it on to _file_grep.
522
    """
523
524
    path = _make_display_path(relpath, path)
6874.2.5 by Jelmer Vernooij
Fix grep.
525
    file_text = tree.get_file_text(tree_path)
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
526
    _file_grep(file_text, path, opts, revno, path_prefix)
0.41.21 by Parth Malwankar
include/exclude working now. tests not added.
527
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
528
0.41.21 by Parth Malwankar
include/exclude working now. tests not added.
529
def _path_in_glob_list(path, glob_list):
530
    for glob in glob_list:
531
        if fnmatch(path, glob):
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
532
            return True
533
    return False
0.41.12 by Parth Malwankar
initial support for working tree grep (no test cases yet!)
534
0.40.117 by Parth Malwankar
cosmetic fix. added two lines between top level functions.
535
0.46.7 by Martin
Move line writing function up the stack so it lasts the whole operation, and clean up some params
536
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.
537
    # test and skip binary files
7027.9.1 by Jelmer Vernooij
Fix all but one remaining grep tests.
538
    if b'\x00' in file.read(1024):
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
539
        if opts.verbose:
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
540
            trace.warning("Binary file '%s' skipped.", path)
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
541
        return
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
542
7143.15.2 by Jelmer Vernooij
Run autopep8.
543
    file.seek(0)  # search from beginning
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
544
545
    found = False
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
546
    if opts.fixed_string:
547
        pattern = opts.pattern.encode(_user_encoding, 'replace')
0.46.1 by Martin
Make -Fi use regexps for re.IGNORECASE rather than double str.lower
548
        for line in file:
549
            if pattern in line:
550
                found = True
551
                break
7143.15.2 by Jelmer Vernooij
Run autopep8.
552
    else:  # not fixed_string
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
553
        for line in file:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
554
            if opts.patternc.search(line):
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
555
                found = True
0.40.116 by Parth Malwankar
optimization for wtree list-only grep to avoid full file read.
556
                break
557
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
558
    if (opts.files_with_matches and found) or \
7143.15.2 by Jelmer Vernooij
Run autopep8.
559
            (opts.files_without_match and not found):
0.40.118 by Parth Malwankar
further optimization of _file_grep_list_only_wtree.
560
        if path_prefix and path_prefix != '.':
561
            # user has passed a dir arg, show that as result prefix
562
            path = osutils.pathjoin(path_prefix, path)
0.46.18 by Martin
Fix another, previously existing issue with colour and match-only
563
        opts.outputter.get_writer(path, None, None)()
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
564
565
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
566
class _Outputter(object):
567
    """Precalculate formatting based on options given
568
569
    The idea here is to do this work only once per run, and finally return a
570
    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
571
    """
7143.15.2 by Jelmer Vernooij
Run autopep8.
572
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
573
    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
574
        self.outf = opts.outf
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
575
        if use_cache:
576
            # self.cache is used to cache results for dir grep based on fid.
577
            # If the fid is does not change between results, it means that
578
            # the result will be the same apart from revno. In such a case
579
            # we avoid getting file chunks from repo and grepping. The result
580
            # is just printed by replacing old revno with new one.
581
            self.cache = {}
582
        else:
583
            self.cache = None
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
584
        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
585
586
        if opts.show_color:
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
587
            if no_line:
588
                self.get_writer = self._get_writer_plain
589
            elif opts.fixed_string:
7027.9.1 by Jelmer Vernooij
Fix all but one remaining grep tests.
590
                self._old = opts.pattern
591
                self._new = color_string(opts.pattern, FG.BOLD_RED)
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
592
                self.get_writer = self._get_writer_fixed_highlighted
593
            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
594
                flags = opts.patternc.flags
7143.15.2 by Jelmer Vernooij
Run autopep8.
595
                self._sub = re.compile(
596
                    opts.pattern.join(("((?:", ")+)")), flags).sub
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
597
                self._highlight = color_string("\\1", FG.BOLD_RED)
598
                self.get_writer = self._get_writer_regexp_highlighted
599
            path_start = FG.MAGENTA
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
600
            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
601
            sep = color_string(':', FG.BOLD_CYAN)
602
            rev_sep = color_string('~', FG.BOLD_YELLOW)
603
        else:
604
            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
605
            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
606
            sep = ":"
607
            rev_sep = "~"
608
609
        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
610
        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
611
            parts.extend([rev_sep, "%(revno)s"])
0.46.13 by Martin
Split format string into two parts for non-cached operations too
612
        self._format_initial = "".join(parts)
613
        parts = []
0.46.17 by Martin
Fix previously untested issue with colour and match-only, and test a related issue
614
        if no_line:
615
            if not opts.print_revno:
616
                parts.append(path_end)
617
        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
618
            if opts.line_number:
0.46.13 by Martin
Split format string into two parts for non-cached operations too
619
                parts.extend([sep, "%(lineno)s"])
620
            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
621
        parts.append(opts.eol_marker)
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
622
        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
623
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
624
    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
625
        """Get function for writing uncoloured output"""
0.46.13 by Martin
Split format string into two parts for non-cached operations too
626
        per_line = self._format_perline
7143.15.2 by Jelmer Vernooij
Run autopep8.
627
        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
628
        write = self.outf.write
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
629
        if self.cache is not None and cache_id is not None:
630
            result_list = []
631
            self.cache[cache_id] = path, result_list
632
            add_to_cache = result_list.append
7143.15.2 by Jelmer Vernooij
Run autopep8.
633
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
634
            def _line_cache_and_writer(**kwargs):
635
                """Write formatted line and cache arguments"""
0.46.12 by Martin
Split format string for cache to only store a string, not a dict
636
                end = per_line % kwargs
637
                add_to_cache(end)
638
                write(start + end)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
639
            return _line_cache_and_writer
7143.15.2 by Jelmer Vernooij
Run autopep8.
640
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
641
        def _line_writer(**kwargs):
642
            """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
643
            write(start + per_line % kwargs)
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
644
        return _line_writer
645
0.46.11 by Martin
Add method to outputter for writing cached lines
646
    def write_cached_lines(self, cache_id, revno):
647
        """Write cached results out again for new revision"""
648
        cached_path, cached_matches = self.cache[cache_id]
7143.15.2 by Jelmer Vernooij
Run autopep8.
649
        start = self._format_initial % {"path": cached_path, "revno": revno}
0.46.11 by Martin
Add method to outputter for writing cached lines
650
        write = self.outf.write
0.46.12 by Martin
Split format string for cache to only store a string, not a dict
651
        for end in cached_matches:
652
            write(start + end)
0.46.11 by Martin
Add method to outputter for writing cached lines
653
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
654
    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
655
        """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
656
        _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
657
        sub, highlight = self._sub, self._highlight
7143.15.2 by Jelmer Vernooij
Run autopep8.
658
0.46.8 by Martin
Move pattern highlighting out of _file_grep and into the line writing code
659
        def _line_writer_regexp_highlighted(line, **kwargs):
660
            """Write formatted line with matched pattern highlighted"""
661
            return _line_writer(line=sub(highlight, line), **kwargs)
662
        return _line_writer_regexp_highlighted
663
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
664
    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
665
        """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
666
        _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
667
        old, new = self._old, self._new
7143.15.2 by Jelmer Vernooij
Run autopep8.
668
0.46.9 by Martin
Give in and make formatter a class so path and revno only need to be passed once per file
669
        def _line_writer_fixed_highlighted(line, **kwargs):
670
            """Write formatted line with string searched for highlighted"""
671
            return _line_writer(line=line.replace(old, new), **kwargs)
672
        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
673
674
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
675
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.
676
    # test and skip binary files
6977.2.1 by Jelmer Vernooij
Require that get_file implementations are contect managers, simplify file handling in transform.
677
    if b'\x00' in file_text[:1024]:
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
678
        if opts.verbose:
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
679
            trace.warning("Binary file '%s' skipped.", path)
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
680
        return
0.41.9 by Parth Malwankar
refactored code towards support for working tree grep.
681
0.40.52 by Parth Malwankar
code cleanup and documentation
682
    if path_prefix and path_prefix != '.':
683
        # user has passed a dir arg, show that as result prefix
684
        path = osutils.pathjoin(path_prefix, path)
685
0.46.21 by Martin
Fix and test bytes/unicode issue but there's more to do in this area
686
    # GZ 2010-06-07: There's no actual guarentee the file contents will be in
687
    #                the user encoding, but we have to guess something and it
688
    #                is a reasonable default without a better mechanism.
689
    file_encoding = _user_encoding
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
690
    pattern = opts.pattern.encode(_user_encoding, 'replace')
0.43.8 by Parth Malwankar
added color for regex pattern.
691
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
692
    writeline = opts.outputter.get_writer(path, revno, cache_id)
0.40.9 by Parth Malwankar
factored out grep related code to grep.py
693
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
694
    if opts.files_with_matches or opts.files_without_match:
0.46.2 by Martin
Remove redundant code on files_with_matches path in _file_grep
695
        if opts.fixed_string:
6691.1.5 by Jelmer Vernooij
Drop support for Python <= 2.5.
696
            found = pattern in file_text
0.40.112 by Parth Malwankar
support for -l, --files-with-matches. no tests yet.
697
        else:
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
698
            search = opts.patternc.search
7027.9.1 by Jelmer Vernooij
Fix all but one remaining grep tests.
699
            if b"$" not in pattern:
0.47.2 by Martin
Use whole text search for match only cases where possible as well
700
                found = search(file_text) is not None
701
            else:
702
                for line in file_text.splitlines():
703
                    if search(line):
704
                        found = True
705
                        break
706
                else:
707
                    found = False
0.43.1 by Parth Malwankar
added GrepOptions object for easy parameter passing
708
        if (opts.files_with_matches and found) or \
709
                (opts.files_without_match and not found):
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
710
            writeline()
0.46.19 by Martin
Minor pokes, fixes a bug with working tree optimisation and binary files
711
    elif opts.fixed_string:
0.47.1 by Martin
Implement whole text search for fast failure on no match
712
        # Fast path for no match, search through the entire file at once rather
6619.3.25 by Jelmer Vernooij
Drop some old dependency checks.
713
        # than a line at a time. <http://effbot.org/zone/stringlib.htm>
714
        i = file_text.find(pattern)
715
        if i == -1:
716
            return
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
717
        b = file_text.rfind(b"\n", 0, i) + 1
6619.3.25 by Jelmer Vernooij
Drop some old dependency checks.
718
        if opts.line_number:
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
719
            start = file_text.count(b"\n", 0, b) + 1
6619.3.25 by Jelmer Vernooij
Drop some old dependency checks.
720
        file_text = file_text[b:]
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
721
        if opts.line_number:
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
722
            for index, line in enumerate(file_text.splitlines()):
723
                if pattern in line:
0.40.137 by Parth Malwankar
(Martin [gz]) Add seperate output formatter
724
                    line = line.decode(file_encoding, 'replace')
7143.15.2 by Jelmer Vernooij
Run autopep8.
725
                    writeline(lineno=index + start, line=line)
0.46.5 by Martin
Delete now redundant duplicated loops in _file_grep
726
        else:
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
727
            for line in file_text.splitlines():
728
                if pattern in line:
0.40.137 by Parth Malwankar
(Martin [gz]) Add seperate output formatter
729
                    line = line.decode(file_encoding, 'replace')
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
730
                    writeline(line=line)
0.40.63 by Parth Malwankar
performance: moved conditionals out of core loop.
731
    else:
0.47.1 by Martin
Implement whole text search for fast failure on no match
732
        # Fast path on no match, the re module avoids bad behaviour in most
733
        # standard cases, but perhaps could try and detect backtracking
734
        # patterns here and avoid whole text search in those cases
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
735
        search = opts.patternc.search
7027.9.1 by Jelmer Vernooij
Fix all but one remaining grep tests.
736
        if b"$" not in pattern:
0.47.1 by Martin
Implement whole text search for fast failure on no match
737
            # GZ 2010-06-05: Grr, re.MULTILINE can't save us when searching
738
            #                through revisions as bazaar returns binary mode
739
            #                and trailing \r breaks $ as line ending match
740
            m = search(file_text)
741
            if m is None:
742
                return
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
743
            b = file_text.rfind(b"\n", 0, m.start()) + 1
0.47.1 by Martin
Implement whole text search for fast failure on no match
744
            if opts.line_number:
7027.3.3 by Jelmer Vernooij
Add some more bees; support writing both bytes and unicode strings in build_tree_contents.
745
                start = file_text.count(b"\n", 0, b) + 1
0.47.4 by Martin
Scale back no-match fast path to avoid some behaviour changes with line endings
746
            file_text = file_text[b:]
0.47.3 by Martin
Fix previously untested bug with regexp and line numbers introduced by optimisation
747
        else:
748
            start = 1
0.46.15 by Martin
Swap fixed_string/line_number branches in _file_grep
749
        if opts.line_number:
750
            for index, line in enumerate(file_text.splitlines()):
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
751
                if search(line):
0.40.137 by Parth Malwankar
(Martin [gz]) Add seperate output formatter
752
                    line = line.decode(file_encoding, 'replace')
7143.15.2 by Jelmer Vernooij
Run autopep8.
753
                    writeline(lineno=index + start, line=line)
0.40.83 by Parth Malwankar
added support for -F/--fixed-string.
754
        else:
755
            for line in file_text.splitlines():
0.46.16 by Martin
Save an attribute lookup on regexp object in inner loops
756
                if search(line):
0.40.137 by Parth Malwankar
(Martin [gz]) Add seperate output formatter
757
                    line = line.decode(file_encoding, 'replace')
0.46.10 by Martin
Move caching mechanism onto outputter rather than passing around dicts and lists
758
                    writeline(line=line)