~abentley/bzrtools/bzrtools.dev

0.1.18 by Michael Ellerman
Split out HunkSelector class.
1
#!/usr/bin/python
2
3
import sys
4
5
class HunkSelector:
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
    ]
31
32
    def __init__(self, patches):
33
        self.patches = patches
34
        self.total_hunks = 0
35
        for patch in patches:
36
            for hunk in patch.hunks:
37
                # everything's shelved by default
38
                hunk.selected = True
39
                self.total_hunks += 1
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
49
        for patch in self.patches:
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
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':
74
                    hunk.selected = True
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
89
90
    def select(self):
91
        if self.total_hunks == 0:
92
            return []
93
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
116
117
        for patch in self.patches:
118
            tmp = []
119
            for hunk in patch.hunks:
120
                if hunk.selected:
121
                    tmp.append(hunk)
122
            patch.hunks = tmp
123
124
        tmp = []
125
        for patch in self.patches:
126
            if len(patch.hunks):
127
                tmp.append(patch)
128
        self.patches = tmp
129
130
        return self.patches
131
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)