~bzr-pqm/bzr/bzr.dev

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