~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to hunk_selector.py

  • Committer: Aaron Bentley
  • Date: 2005-11-11 17:43:12 UTC
  • Revision ID: aaron.bentley@utoronto.ca-20051111174312-1c627d82a07bf8fd
Added patch for tab-in-patch-filename support

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
2
 
3
 
from userinteractor import UserInteractor, UserOption
 
3
import sys
4
4
 
5
5
class HunkSelector:
6
 
    strings = {}
 
6
    class Option:
 
7
        def __init__(self, char, action, help, default=False):
 
8
            self.char = char
 
9
            self.action = action
 
10
            self.default = default
 
11
            self.help = help
 
12
 
 
13
    standard_options = [
 
14
        Option('n', 'shelve', 'shelve this change for the moment.',
 
15
            default=True),
 
16
        Option('y', 'keep', 'keep this change in your tree.'),
 
17
        Option('d', 'done', 'done, skip to the end.'),
 
18
        Option('i', 'invert', 'invert the current selection of all hunks.'),
 
19
        Option('s', 'status', 'show status of hunks.'),
 
20
        Option('q', 'quit', 'quit')
 
21
    ]
 
22
 
 
23
    end_options = [
 
24
        Option('y', 'continue', 'proceed to shelve selected changes.',
 
25
            default=True),
 
26
        Option('r', 'restart', 'restart the hunk selection loop.'),
 
27
        Option('s', 'status', 'show status of hunks.'),
 
28
        Option('i', 'invert', 'invert the current selection of all hunks.'),
 
29
        Option('q', 'quit', 'quit')
 
30
    ]
7
31
 
8
32
    def __init__(self, patches):
9
 
        self.standard_options = [
10
 
            UserOption('y', self._selected, self.strings['select_desc'],
11
 
                default=True),
12
 
            UserOption('n', self._unselected, self.strings['unselect_desc']),
13
 
            UserOption('d', UserInteractor.FINISH, 'done, skip to the end.'),
14
 
            UserOption('i', self._invert,
15
 
                'invert the current selection status of all hunks.'),
16
 
            UserOption('s', self._status,
17
 
                'show selection status of all hunks.'),
18
 
            UserOption('q', UserInteractor.QUIT, 'quit')
19
 
        ]
20
 
 
21
 
        self.end_options = [
22
 
            UserOption('y', UserInteractor.FINISH, self.strings['finish_desc'],
23
 
                default=True),
24
 
            UserOption('r', UserInteractor.RESTART,
25
 
                'restart the hunk selection loop.'),
26
 
            UserOption('s', self._status,
27
 
                'show selection status of all hunks.'),
28
 
            UserOption('i', self._invert,
29
 
                'invert the current selection status of all hunks.'),
30
 
            UserOption('q', UserInteractor.QUIT, 'quit')
31
 
        ]
32
 
 
33
33
        self.patches = patches
34
34
        self.total_hunks = 0
35
 
 
36
 
        self.interactor = UserInteractor()
37
 
        self.interactor.set_item_callback(self._hunk_callback)
38
 
        self.interactor.set_start_callback(self._start_callback)
39
 
        self.interactor.set_end_callback(self._end_callback)
40
 
 
41
35
        for patch in patches:
42
36
            for hunk in patch.hunks:
43
 
                # everything's selected by default
 
37
                # everything's shelved by default
44
38
                hunk.selected = True
45
39
                self.total_hunks += 1
46
 
                # we need a back pointer in the callbacks
47
 
                hunk.patch = patch
48
 
                self.interactor.add_item(hunk)
49
 
 
50
 
    # Called at the start of the main loop
51
 
    def _start_callback(self):
52
 
        self.last_printed = -1
53
 
        self.interactor.set_prompt(self.strings['prompt'])
54
 
        self.interactor.set_options(self.standard_options)
55
 
 
56
 
    # Called at the end of the item loop, return False to indicate that the
57
 
    # interaction isn't finished and the confirmation prompt should be displayed
58
 
    def _end_callback(self):
59
 
        self._status()
60
 
        self.interactor.set_prompt(self.strings['end_prompt'])
61
 
        self.interactor.set_options(self.end_options)
62
 
        return False
63
 
 
64
 
    # Called once for each hunk
65
 
    def _hunk_callback(self, hunk, count):
66
 
        if self.last_printed != count:
67
 
            print hunk.patch.get_header(), hunk
68
 
            self.last_printed = count
69
 
 
70
 
        if hunk.selected:
71
 
            self.interactor.get_option('y').default = True
72
 
            self.interactor.get_option('n').default = False
73
 
        else:
74
 
            self.interactor.get_option('y').default = False
75
 
            self.interactor.get_option('n').default = True
76
 
 
77
 
    # The user chooses to (un)shelve a hunk
78
 
    def _selected(self, hunk):
79
 
        hunk.selected = True
80
 
        return True
81
 
 
82
 
    # The user chooses to keep a hunk
83
 
    def _unselected(self, hunk):
84
 
        hunk.selected = False
85
 
        return True
86
 
 
87
 
    # The user chooses to invert the selection
88
 
    def _invert(self, hunk):
 
40
 
 
41
    def __get_option(self, char):
 
42
        for opt in self.standard_options:
 
43
            if opt.char == char:
 
44
                return opt
 
45
        raise Exception('Option "%s" not found!' % char)
 
46
 
 
47
    def __select_loop(self):
 
48
        j = 0
89
49
        for patch in self.patches:
90
 
            for hunk in patch.hunks:
91
 
                if hunk.__dict__.has_key('selected'):
92
 
                    hunk.selected = not hunk.selected
 
50
            i = 0
 
51
            lasti = -1
 
52
            while i < len(patch.hunks):
 
53
                hunk = patch.hunks[i]
 
54
                if lasti != i:
 
55
                    print patch.get_header(), hunk
 
56
                    j += 1
 
57
                lasti = i
 
58
 
 
59
                prompt = 'Keep this change? (%d of %d)' \
 
60
                            % (j, self.total_hunks)
 
61
 
 
62
                if hunk.selected:
 
63
                    self.__get_option('n').default = True
 
64
                    self.__get_option('y').default = False
93
65
                else:
 
66
                    self.__get_option('n').default = False
 
67
                    self.__get_option('y').default = True
 
68
 
 
69
                action = self.__ask_user(prompt, self.standard_options)
 
70
 
 
71
                if action == 'keep':
 
72
                    hunk.selected = False
 
73
                elif action == 'shelve':
94
74
                    hunk.selected = True
95
 
        self._status()
96
 
        return False
97
 
 
98
 
    # The user wants to see the status
99
 
    def _status(self, hunk=None):
100
 
        print '\nStatus:'
101
 
        for patch in self.patches:
102
 
            print '  %s' % patch.oldname
103
 
            selected = 0
104
 
            unselected = 0
105
 
            for hunk in patch.hunks:
106
 
                if hunk.selected:
107
 
                    selected += 1
108
 
                else:
109
 
                    unselected += 1
110
 
 
111
 
            print '  ', self.strings['status_selected'] % selected
112
 
            print '  ', self.strings['status_unselected'] % unselected
113
 
            print
114
 
 
115
 
        # Tell the interactor we're not done with this item
116
 
        return False
 
75
                elif action == 'done':
 
76
                    return True
 
77
                elif action == 'invert':
 
78
                    self.__invert_selection()
 
79
                    self.__show_status()
 
80
                    continue
 
81
                elif action == 'status':
 
82
                    self.__show_status()
 
83
                    continue
 
84
                elif action == 'quit':
 
85
                    return False
 
86
 
 
87
                i += 1
 
88
        return True
117
89
 
118
90
    def select(self):
119
91
        if self.total_hunks == 0:
120
92
            return []
121
93
 
122
 
        if not self.interactor.interact():
123
 
            # False from interact means they quit
124
 
            return []
 
94
        done = False
 
95
        while not done:
 
96
            if not self.__select_loop():
 
97
                return []
 
98
 
 
99
            while True:
 
100
                self.__show_status()
 
101
                prompt = "Shelve these changes, or restart?"
 
102
                action = self.__ask_user(prompt, self.end_options)
 
103
 
 
104
                if action == 'continue':
 
105
                    done = True
 
106
                    break
 
107
                elif action == 'quit':
 
108
                    return []
 
109
                elif action == 'status':
 
110
                    self.__show_status()
 
111
                elif action == 'invert':
 
112
                    self.__invert_selection()
 
113
                elif action == 'restart':
 
114
                    break
 
115
 
125
116
 
126
117
        for patch in self.patches:
127
118
            tmp = []
138
129
 
139
130
        return self.patches
140
131
 
141
 
class ShelveHunkSelector(HunkSelector):
142
 
    def __init__(self, patches):
143
 
        self.strings = {}
144
 
        self.strings['status_selected'] = '%d hunks to be shelved'
145
 
        self.strings['status_unselected'] = '%d hunks to be kept'
146
 
        self.strings['select_desc'] = 'shelve this change.'
147
 
        self.strings['unselect_desc'] = 'keep this change in your tree.'
148
 
        self.strings['finish_desc'] = 'shelve selected changes.'
149
 
        self.strings['prompt'] = 'Shelve this change? (%(count)d of %(total)d)'
150
 
        self.strings['end_prompt'] = 'Shelve these changes?'
151
 
        HunkSelector.__init__(self, patches)
152
 
 
153
 
class UnshelveHunkSelector(HunkSelector):
154
 
    def __init__(self, patches):
155
 
        self.strings = {}
156
 
        self.strings['status_selected'] = '%d hunks to be unshelved'
157
 
        self.strings['status_unselected'] = '%d hunks left on shelf'
158
 
        self.strings['select_desc'] = 'unshelve this change.'
159
 
        self.strings['unselect_desc'] = 'leave this change on the shelf.'
160
 
        self.strings['finish_desc'] = 'unshelve selected changes.'
161
 
        self.strings['prompt'] = 'Unshelve this change? ' \
162
 
            '(%(count)d of %(total)d)'
163
 
        self.strings['end_prompt'] = 'Unshelve these changes?'
164
 
        HunkSelector.__init__(self, patches)
 
132
    def __invert_selection(self):
 
133
        for patch in self.patches:
 
134
            for hunk in patch.hunks:
 
135
                if hunk.__dict__.has_key('selected'):
 
136
                    hunk.selected = not hunk.selected
 
137
                else:
 
138
                    hunk.selected = True
 
139
 
 
140
    def __show_status(self):
 
141
        print '\nStatus:'
 
142
        for patch in self.patches:
 
143
            print '  %s' % patch.oldname
 
144
            shelve = 0
 
145
            keep = 0
 
146
            for hunk in patch.hunks:
 
147
                if hunk.selected:
 
148
                    shelve += 1
 
149
                else:
 
150
                    keep += 1
 
151
 
 
152
            print '    %d hunks to be shelved' % shelve
 
153
            print '    %d hunks to be kept' % keep
 
154
            print
 
155
 
 
156
    if sys.platform == "win32":
 
157
        import msvcrt
 
158
        def __getchar(self):
 
159
            return msvcrt.getche()
 
160
    else:
 
161
        def __getchar(self):
 
162
            import tty
 
163
            import termios
 
164
            fd = sys.stdin.fileno()
 
165
            settings = termios.tcgetattr(fd)
 
166
            try:
 
167
                tty.setraw(fd)
 
168
                ch = sys.stdin.read(1)
 
169
            finally:
 
170
                termios.tcsetattr(fd, termios.TCSADRAIN, settings)
 
171
            return ch
 
172
 
 
173
    def __ask_user(self, prompt, options):
 
174
        while True:
 
175
            sys.stdout.write(prompt)
 
176
            sys.stdout.write(' [')
 
177
            for opt in options:
 
178
                if opt.default:
 
179
                    default = opt
 
180
                sys.stdout.write(opt.char)
 
181
            sys.stdout.write('?] (%s): ' % default.char)
 
182
 
 
183
            response = self.__getchar()
 
184
 
 
185
            # default, which we see as newline, is 'n'
 
186
            if response in ['\n', '\r', '\r\n']:
 
187
                response = default.char
 
188
 
 
189
            print response # because echo is off
 
190
 
 
191
            for opt in options:
 
192
                if opt.char == response:
 
193
                    return opt.action
 
194
 
 
195
            for opt in options:
 
196
                print '  %s - %s' % (opt.char, opt.help)