/brz/remove-bazaar

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