~abentley/bzrtools/bzrtools.dev

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