record: refactor the prompt loop
authorPatrick Mezard <pmezard@gmail.com>
Sun, 23 Jan 2011 15:21:37 +0100
changeset 13293 ad1b46e4a575
parent 13292 d32e09769048
child 13294 98f0adfc89e3
record: refactor the prompt loop The previous loop was iterating over a mixed header/hunk stream. It may have been more generic in the sense every item in the stream could trigger a prompt but it required more work to skip items properly. It can be rewritten in a more intuitive way by looping on files then looping on hunks.
hgext/record.py
--- a/hgext/record.py	Sun Jan 23 15:21:34 2011 +0100
+++ b/hgext/record.py	Sun Jan 23 15:21:37 2011 +0100
@@ -178,7 +178,7 @@
         return '<hunk %r@%d>' % (self.filename(), self.fromline)
 
 def parsepatch(fp):
-    """patch -> [] of hunks """
+    """patch -> [] of headers -> [] of hunks """
     class parser(object):
         """patch parsing state machine"""
         def __init__(self):
@@ -189,7 +189,7 @@
             self.context = []
             self.before = []
             self.hunk = []
-            self.stream = []
+            self.headers = []
 
         def addrange(self, limits):
             fromstart, fromend, tostart, toend, proc = limits
@@ -202,7 +202,6 @@
                 h = hunk(self.header, self.fromline, self.toline, self.proc,
                          self.before, self.hunk, context)
                 self.header.hunks.append(h)
-                self.stream.append(h)
                 self.fromline += len(self.before) + h.removed
                 self.toline += len(self.before) + h.added
                 self.before = []
@@ -219,12 +218,12 @@
         def newfile(self, hdr):
             self.addcontext([])
             h = header(hdr)
-            self.stream.append(h)
+            self.headers.append(h)
             self.header = h
 
         def finished(self):
             self.addcontext([])
-            return self.stream
+            return self.headers
 
         transitions = {
             'file': {'context': addcontext,
@@ -253,18 +252,8 @@
         state = newstate
     return p.finished()
 
-def filterpatch(ui, chunks):
+def filterpatch(ui, headers):
     """Interactively filter patch chunks into applied-only chunks"""
-    def consumefile(chunks):
-        """fetch next portion from chunks until a 'header' is seen
-        NB: header == new-file mark
-        """
-        consumed = []
-        while chunks:
-            if isinstance(chunks[-1], header):
-                break
-            consumed.append(chunks.pop())
-        return consumed
 
     def prompt(skipfile, skipall, query):
         """prompt query, and process base inputs
@@ -315,43 +304,39 @@
                 raise util.Abort(_('user quit'))
             return ret, skipfile, skipall
 
-    chunks = list(chunks)
-    chunks.reverse()
     seen = set()
     applied = {}        # 'filename' -> [] of chunks
     skipfile, skipall = None, None
-    pos, total = 0, len(chunks) - 1
-    while chunks:
-        pos = total - len(chunks) + 1
-        chunk = chunks.pop()
-        if isinstance(chunk, header):
-            # new-file mark
-            skipfile = None
-            fixoffset = 0
-            hdr = ''.join(chunk.header)
-            if hdr in seen:
-                consumefile(chunks)
-                continue
-            seen.add(hdr)
-            if skipall is None:
-                chunk.pretty(ui)
-            msg = (_('examine changes to %s?') %
-                   _(' and ').join(map(repr, chunk.files())))
-            r, skipfile, skipall = prompt(skipfile, skipall, msg)
-            if r:
-                applied[chunk.filename()] = [chunk]
-                if chunk.allhunks():
-                    applied[chunk.filename()] += consumefile(chunks)
-            else:
-                consumefile(chunks)
-        else:
-            # new hunk
+    # XXX: operation count is weird: it counts headers and hunks
+    # except for the first header. It probably comes from the previous
+    # mixed header/hunk stream representation.
+    pos, total = -1, sum((len(h.hunks) + 1) for h in headers) - 1
+    for h in headers:
+        pos += len(h.hunks) + 1
+        skipfile = None
+        fixoffset = 0
+        hdr = ''.join(h.header)
+        if hdr in seen:
+            continue
+        seen.add(hdr)
+        if skipall is None:
+            h.pretty(ui)
+        msg = (_('examine changes to %s?') %
+               _(' and ').join(map(repr, h.files())))
+        r, skipfile, skipall = prompt(skipfile, skipall, msg)
+        if not r:
+            continue
+        applied[h.filename()] = [h]
+        if h.allhunks():
+            applied[h.filename()] += h.hunks
+            continue
+        for i, chunk in enumerate(h.hunks):
             if skipfile is None and skipall is None:
                 chunk.pretty(ui)
             msg = (total == 1
                    and (_('record this change to %r?') % chunk.filename())
                    or (_('record change %d/%d to %r?') %
-                       (pos, total, chunk.filename())))
+                       (pos - len(h.hunks) + i + 1, total, chunk.filename())))
             r, skipfile, skipall = prompt(skipfile, skipall, msg)
             if r:
                 if fixoffset: