Refactor log ui buffering and patch display
authorMatt Mackall <mpm@selenic.com>
Mon, 13 Nov 2006 13:26:57 -0600
changeset 3645 b984dcb1df71
parent 3644 b7547efe78fb
child 3646 069f3acdad6c
child 3667 d5032b951c5c
Refactor log ui buffering and patch display
mercurial/cmdutil.py
mercurial/commands.py
tests/test-remove.out
--- a/mercurial/cmdutil.py	Mon Nov 13 13:26:57 2006 -0600
+++ b/mercurial/cmdutil.py	Mon Nov 13 13:26:57 2006 -0600
@@ -9,7 +9,7 @@
 from node import *
 from i18n import gettext as _
 demandload(globals(), 'os sys')
-demandload(globals(), 'mdiff util templater cStringIO')
+demandload(globals(), 'mdiff util templater cStringIO patch')
 
 revrangesep = ':'
 
@@ -196,15 +196,63 @@
             if not dry_run:
                 repo.copy(old, new, wlock=wlock)
 
+class uibuffer(object):
+    # Implement and delegate some ui protocol.  Save hunks of
+    # output for later display in the desired order.
+    def __init__(self, ui):
+        self.ui = ui
+        self.hunk = {}
+        self.header = {}
+        self.quiet = ui.quiet
+        self.verbose = ui.verbose
+        self.debugflag = ui.debugflag
+        self.lastheader = None
+    def note(self, *args):
+        if self.verbose:
+            self.write(*args)
+    def status(self, *args):
+        if not self.quiet:
+            self.write(*args)
+    def debug(self, *args):
+        if self.debugflag:
+            self.write(*args)
+    def write(self, *args):
+        self.hunk.setdefault(self.rev, []).extend(args)
+    def write_header(self, *args):
+        self.header.setdefault(self.rev, []).extend(args)
+    def mark(self, rev):
+        self.rev = rev
+    def flush(self, rev):
+        if rev in self.header:
+            h = "".join(self.header[rev])
+            if h != self.lastheader:
+                self.lastheader = h
+                self.ui.write(h)
+            del self.header[rev]
+        if rev in self.hunk:
+            self.ui.write("".join(self.hunk[rev]))
+            del self.hunk[rev]
+            return 1
+        return 0
+
 class changeset_printer(object):
     '''show changeset information when templating not requested.'''
 
-    def __init__(self, ui, repo):
+    def __init__(self, ui, repo, patch, buffered):
         self.ui = ui
         self.repo = repo
+        self.buffered = buffered
+        self.patch = patch
+        if buffered:
+            self.ui = uibuffer(ui)
+
+    def flush(self, rev):
+        return self.ui.flush(rev)
 
     def show(self, rev=0, changenode=None, brinfo=None, copies=None):
         '''show a single changeset or file revision'''
+        if self.buffered:
+            self.ui.mark(rev)
         log = self.repo.changelog
         if changenode is None:
             changenode = log.node(rev)
@@ -280,17 +328,23 @@
                               description.splitlines()[0])
         self.ui.write("\n")
 
-class changeset_templater(object):
+        self.showpatch(changenode)
+
+    def showpatch(self, node):
+        if self.patch:
+            prev = self.repo.changelog.parents(node)[0]
+            patch.diff(self.repo, prev, node, fp=self.ui)
+            self.ui.write("\n")
+
+class changeset_templater(changeset_printer):
     '''format changeset information.'''
 
-    def __init__(self, ui, repo, mapfile, dest=None):
+    def __init__(self, ui, repo, patch, mapfile, buffered):
+        changeset_printer.__init__(self, ui, repo, patch, buffered)
         self.t = templater.templater(mapfile, templater.common_filters,
                                      cache={'parent': '{rev}:{node|short} ',
                                             'manifest': '{rev}:{node|short}',
                                             'filecopy': '{name} ({source})'})
-        self.ui = ui
-        self.dest = dest
-        self.repo = repo
 
     def use_template(self, t):
         '''set template string to use'''
@@ -298,6 +352,8 @@
 
     def show(self, rev=0, changenode=None, brinfo=None, copies=[], **props):
         '''show a single changeset or file revision'''
+        if self.buffered:
+            self.ui.mark(rev)
         log = self.repo.changelog
         if changenode is None:
             changenode = log.node(rev)
@@ -440,7 +496,6 @@
         props.update(defprops)
 
         try:
-            dest = self.dest or self.ui
             if self.ui.debugflag and 'header_debug' in self.t:
                 key = 'header_debug'
             elif self.ui.quiet and 'header_quiet' in self.t:
@@ -452,7 +507,11 @@
             else:
                 key = ''
             if key:
-                dest.write_header(templater.stringify(self.t(key, **props)))
+                h = templater.stringify(self.t(key, **props))
+                if self.buffered:
+                    self.ui.write_header(h)
+                else:
+                    self.ui.write(h)
             if self.ui.debugflag and 'changeset_debug' in self.t:
                 key = 'changeset_debug'
             elif self.ui.quiet and 'changeset_quiet' in self.t:
@@ -461,7 +520,8 @@
                 key = 'changeset_verbose'
             else:
                 key = 'changeset'
-            dest.write(templater.stringify(self.t(key, **props)))
+            self.ui.write(templater.stringify(self.t(key, **props)))
+            self.showpatch(changenode)
         except KeyError, inst:
             raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
                                                            inst.args[0]))
@@ -482,7 +542,7 @@
     def __getattr__(self, key):
         return getattr(self.fp, key)
 
-def show_changeset(ui, repo, opts):
+def show_changeset(ui, repo, opts, buffered=False):
     """show one changeset using template or regular display.
 
     Display format will be the first non-empty hit of:
@@ -494,6 +554,7 @@
     regular display via changeset_printer() is done.
     """
     # options
+    patch = opts.get('patch')
     tmpl = opts.get('template')
     mapfile = None
     if tmpl:
@@ -515,10 +576,10 @@
                            or templater.templatepath(mapfile))
                 if mapname: mapfile = mapname
         try:
-            t = changeset_templater(ui, repo, mapfile)
+            t = changeset_templater(ui, repo, patch, mapfile, buffered)
         except SyntaxError, inst:
             raise util.Abort(inst.args[0])
         if tmpl: t.use_template(tmpl)
         return t
-    return changeset_printer(ui, repo)
+    return changeset_printer(ui, repo, patch, buffered)
 
--- a/mercurial/commands.py	Mon Nov 13 13:26:57 2006 -0600
+++ b/mercurial/commands.py	Mon Nov 13 13:26:57 2006 -0600
@@ -1592,10 +1592,6 @@
             if opts['no_merges'] and len(parents) == 2:
                 continue
             displayer.show(changenode=n)
-            if opts['patch']:
-                prev = (parents and parents[0]) or nullid
-                patch.diff(other, prev, n, fp=repo.ui)
-                ui.write("\n")
     finally:
         if hasattr(other, 'close'):
             other.close()
@@ -1672,35 +1668,6 @@
     commit. When the -v/--verbose switch is used, the list of changed
     files and full commit message is shown.
     """
-    class dui(object):
-        # Implement and delegate some ui protocol.  Save hunks of
-        # output for later display in the desired order.
-        def __init__(self, ui):
-            self.ui = ui
-            self.hunk = {}
-            self.header = {}
-            self.quiet = ui.quiet
-            self.verbose = ui.verbose
-            self.debugflag = ui.debugflag
-        def bump(self, rev):
-            self.rev = rev
-            self.hunk[rev] = []
-            self.header[rev] = []
-        def note(self, *args):
-            if self.verbose:
-                self.write(*args)
-        def status(self, *args):
-            if not self.quiet:
-                self.write(*args)
-        def write(self, *args):
-            self.hunk[self.rev].extend(args)
-        def write_header(self, *args):
-            self.header[self.rev].extend(args)
-        def debug(self, *args):
-            if self.debugflag:
-                self.write(*args)
-        def __getattr__(self, key):
-            return getattr(self.ui, key)
 
     getchange = util.cachefunc(lambda r:repo.changectx(r).changeset())
     changeiter, matchfn = walkchangerevs(ui, repo, pats, getchange, opts)
@@ -1755,13 +1722,9 @@
             return ncache[fn].get(dcache[1][fn])
         return None
 
-    displayer = cmdutil.show_changeset(ui, repo, opts)
+    displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
     for st, rev, fns in changeiter:
-        if st == 'window':
-            du = dui(ui)
-            displayer.ui = du
-        elif st == 'add':
-            du.bump(rev)
+        if st == 'add':
             changenode = repo.changelog.node(rev)
             parents = [p for p in repo.changelog.parentrevs(rev)
                        if p != nullrev]
@@ -1794,21 +1757,10 @@
                     if rename:
                         copies.append((fn, rename[0]))
             displayer.show(rev, changenode, brinfo=br, copies=copies)
-            if opts['patch']:
-                if parents:
-                    prev = parents[0]
-                else:
-                    prev = nullrev
-                prev = repo.changelog.node(prev)
-                patch.diff(repo, prev, changenode, match=matchfn, fp=du)
-                du.write("\n\n")
         elif st == 'iter':
             if count == limit: break
-            if du.header[rev]:
-                ui.write_header(*du.header[rev])
-            if du.hunk[rev]:
+            if displayer.flush(rev):
                 count += 1
-                ui.write(*du.hunk[rev])
 
 def manifest(ui, repo, rev=None):
     """output the latest or given revision of the project manifest
@@ -1897,10 +1849,6 @@
         if opts['no_merges'] and len(parents) == 2:
             continue
         displayer.show(changenode=n)
-        if opts['patch']:
-            prev = (parents and parents[0]) or nullid
-            patch.diff(repo, prev, n)
-            ui.write("\n")
 
 def parents(ui, repo, file_=None, rev=None, branches=None, **opts):
     """show the parents of the working dir or revision
@@ -2560,8 +2508,6 @@
                   "please use 'hg branches' instead\n"))
         br = repo.branchlookup([n])
     cmdutil.show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
-    if opts['patch']:
-        patch.diff(repo, repo.changelog.parents(n)[0], n)
 
 def unbundle(ui, repo, fname, **opts):
     """apply a changegroup file
--- a/tests/test-remove.out	Mon Nov 13 13:26:57 2006 -0600
+++ b/tests/test-remove.out	Mon Nov 13 13:26:57 2006 -0600
@@ -37,7 +37,6 @@
 @@ -0,0 +1,1 @@
 +a
 
-
 changeset:   1:a1fce69c50d9
 tag:         tip
 user:        test
@@ -50,7 +49,6 @@
 @@ -1,1 +0,0 @@
 -a
 
-
 not removing a: file has been marked for add (use -f to force removal)
 adding a
 adding b