~abentley/bzrtools/bzrtools.dev

0.3.5 by Aaron Bentley
Add colorizing to shelf
1
import sys
2
0.1.48 by Michael Ellerman
Move UserInteractor into a seperate file.
3
from userinteractor import UserInteractor, UserOption
423.1.4 by Aaron Bentley
Add --no-color option to shelf
4
from errors import NoColor
0.1.58 by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only
5
import copy
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
6
0.1.18 by Michael Ellerman
Split out HunkSelector class.
7
class HunkSelector:
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
8
    strings = {}
9
423.1.4 by Aaron Bentley
Add --no-color option to shelf
10
    def __init__(self, patches, color=None):
11
        if color is True or color is None:
12
            try:
13
                from colordiff import DiffWriter
14
                from terminal import has_ansi_colors
15
                if has_ansi_colors():
16
                    self.diff_stream = DiffWriter(sys.stdout)
17
                else:
18
                    if color is True:
19
                        raise NoColor()
20
                    self.diff_stream = sys.stdout
21
            except ImportError:
22
                if color is True:
23
                    raise NoBzrtoolsColor()
0.3.5 by Aaron Bentley
Add colorizing to shelf
24
                self.diff_stream = sys.stdout
423.1.4 by Aaron Bentley
Add --no-color option to shelf
25
        else:
0.3.5 by Aaron Bentley
Add colorizing to shelf
26
            self.diff_stream = sys.stdout
423.1.4 by Aaron Bentley
Add --no-color option to shelf
27
            
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
28
        self.standard_options = [
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
29
            UserOption('y', self._selected, self.strings['select_desc'],
30
                default=True),
31
            UserOption('n', self._unselected, self.strings['unselect_desc']),
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
32
            UserOption('d', UserInteractor.FINISH, 'done, skip to the end.'),
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
33
            UserOption('i', self._invert,
0.1.49 by Michael Ellerman
Ask "shelve this change" instead of "keep this change", which is hopefully
34
                'invert the current selection status of all hunks.'),
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
35
            UserOption('s', self._status,
36
                'show selection status of all hunks.'),
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
37
            UserOption('q', UserInteractor.QUIT, 'quit')
38
        ]
39
40
        self.end_options = [
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
41
            UserOption('y', UserInteractor.FINISH, self.strings['finish_desc'],
42
                default=True),
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
43
            UserOption('r', UserInteractor.RESTART,
44
                'restart the hunk selection loop.'),
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
45
            UserOption('s', self._status,
46
                'show selection status of all hunks.'),
47
            UserOption('i', self._invert,
0.1.49 by Michael Ellerman
Ask "shelve this change" instead of "keep this change", which is hopefully
48
                'invert the current selection status of all hunks.'),
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
49
            UserOption('q', UserInteractor.QUIT, 'quit')
50
        ]
51
0.1.18 by Michael Ellerman
Split out HunkSelector class.
52
        self.patches = patches
53
        self.total_hunks = 0
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
54
55
        self.interactor = UserInteractor()
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
56
        self.interactor.set_item_callback(self._hunk_callback)
57
        self.interactor.set_start_callback(self._start_callback)
58
        self.interactor.set_end_callback(self._end_callback)
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
59
0.1.18 by Michael Ellerman
Split out HunkSelector class.
60
        for patch in patches:
61
            for hunk in patch.hunks:
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
62
                # everything's selected by default
0.1.18 by Michael Ellerman
Split out HunkSelector class.
63
                hunk.selected = True
64
                self.total_hunks += 1
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
65
                # we need a back pointer in the callbacks
66
                hunk.patch = patch
67
                self.interactor.add_item(hunk)
68
69
    # Called at the start of the main loop
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
70
    def _start_callback(self):
0.1.51 by Michael Ellerman
Restart after a done was broken, and we weren't resetting the print logic at
71
        self.last_printed = -1
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
72
        self.interactor.set_prompt(self.strings['prompt'])
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
73
        self.interactor.set_options(self.standard_options)
74
75
    # Called at the end of the item loop, return False to indicate that the
76
    # interaction isn't finished and the confirmation prompt should be displayed
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
77
    def _end_callback(self):
78
        self._status()
79
        self.interactor.set_prompt(self.strings['end_prompt'])
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
80
        self.interactor.set_options(self.end_options)
81
        return False
82
83
    # Called once for each hunk
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
84
    def _hunk_callback(self, hunk, count):
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
85
        if self.last_printed != count:
0.3.5 by Aaron Bentley
Add colorizing to shelf
86
            self.diff_stream.write(str(hunk.patch.get_header()))
87
            self.diff_stream.write(str(hunk))
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
88
            self.last_printed = count
89
90
        if hunk.selected:
0.1.49 by Michael Ellerman
Ask "shelve this change" instead of "keep this change", which is hopefully
91
            self.interactor.get_option('y').default = True
92
            self.interactor.get_option('n').default = False
93
        else:
94
            self.interactor.get_option('y').default = False
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
95
            self.interactor.get_option('n').default = True
96
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
97
    # The user chooses to (un)shelve a hunk
98
    def _selected(self, hunk):
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
99
        hunk.selected = True
100
        return True
101
102
    # The user chooses to keep a hunk
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
103
    def _unselected(self, hunk):
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
104
        hunk.selected = False
105
        return True
106
107
    # The user chooses to invert the selection
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
108
    def _invert(self, hunk):
0.1.18 by Michael Ellerman
Split out HunkSelector class.
109
        for patch in self.patches:
110
            for hunk in patch.hunks:
111
                if hunk.__dict__.has_key('selected'):
112
                    hunk.selected = not hunk.selected
113
                else:
114
                    hunk.selected = True
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
115
        self._status()
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
116
        return False
0.1.18 by Michael Ellerman
Split out HunkSelector class.
117
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
118
    # The user wants to see the status
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
119
    def _status(self, hunk=None):
0.1.18 by Michael Ellerman
Split out HunkSelector class.
120
        print '\nStatus:'
121
        for patch in self.patches:
122
            print '  %s' % patch.oldname
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
123
            selected = 0
124
            unselected = 0
0.1.18 by Michael Ellerman
Split out HunkSelector class.
125
            for hunk in patch.hunks:
126
                if hunk.selected:
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
127
                    selected += 1
0.1.18 by Michael Ellerman
Split out HunkSelector class.
128
                else:
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
129
                    unselected += 1
0.1.18 by Michael Ellerman
Split out HunkSelector class.
130
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
131
            print '  ', self.strings['status_selected'] % selected
132
            print '  ', self.strings['status_unselected'] % unselected
0.1.18 by Michael Ellerman
Split out HunkSelector class.
133
            print
134
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
135
        # Tell the interactor we're not done with this item
136
        return False
137
138
    def select(self):
0.1.58 by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only
139
        if self.total_hunks == 0 or not self.interactor.interact():
140
            # False from interact means they chose to quit
141
            return ([], [])
142
143
        # Go through each patch and collect all selected/unselected hunks
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
144
        for patch in self.patches:
0.1.58 by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only
145
            patch.selected = []
146
            patch.unselected = []
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
147
            for hunk in patch.hunks:
148
                if hunk.selected:
0.1.58 by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only
149
                    patch.selected.append(hunk)
150
                else:
151
                    patch.unselected.append(hunk)
152
153
        # Now build two lists, one of selected patches the other unselected
154
        selected_patches = []
155
        unselected_patches = []
156
0.1.47 by Michael Ellerman
Refactored HunkSelector heavily to split out a UserInteractor class.
157
        for patch in self.patches:
0.1.58 by Michael Ellerman
Unshelve --pick was broken, because we deleted the whole patch, even when only
158
            if len(patch.selected):
159
                tmp = copy.copy(patch)
160
                tmp.hunks = tmp.selected
161
                del tmp.selected
162
                del tmp.unselected
163
                selected_patches.append(tmp)
164
165
            if len(patch.unselected):
166
                tmp = copy.copy(patch)
167
                tmp.hunks = tmp.unselected
168
                del tmp.selected
169
                del tmp.unselected
170
                unselected_patches.append(tmp)
171
172
        return (selected_patches, unselected_patches)
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
173
174
class ShelveHunkSelector(HunkSelector):
423.1.4 by Aaron Bentley
Add --no-color option to shelf
175
    def __init__(self, patches, color=None):
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
176
        self.strings = {}
177
        self.strings['status_selected'] = '%d hunks to be shelved'
178
        self.strings['status_unselected'] = '%d hunks to be kept'
179
        self.strings['select_desc'] = 'shelve this change.'
180
        self.strings['unselect_desc'] = 'keep this change in your tree.'
181
        self.strings['finish_desc'] = 'shelve selected changes.'
182
        self.strings['prompt'] = 'Shelve this change? (%(count)d of %(total)d)'
183
        self.strings['end_prompt'] = 'Shelve these changes?'
423.1.4 by Aaron Bentley
Add --no-color option to shelf
184
        HunkSelector.__init__(self, patches, color)
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
185
186
class UnshelveHunkSelector(HunkSelector):
423.1.4 by Aaron Bentley
Add --no-color option to shelf
187
    def __init__(self, patches, color=None):
0.1.56 by Michael Ellerman
Make HunkSelector agnostic as to whether it's selecting for shelving or
188
        self.strings = {}
189
        self.strings['status_selected'] = '%d hunks to be unshelved'
190
        self.strings['status_unselected'] = '%d hunks left on shelf'
191
        self.strings['select_desc'] = 'unshelve this change.'
192
        self.strings['unselect_desc'] = 'leave this change on the shelf.'
193
        self.strings['finish_desc'] = 'unshelve selected changes.'
194
        self.strings['prompt'] = 'Unshelve this change? ' \
195
            '(%(count)d of %(total)d)'
196
        self.strings['end_prompt'] = 'Unshelve these changes?'
423.1.4 by Aaron Bentley
Add --no-color option to shelf
197
        HunkSelector.__init__(self, patches, color)