absorb: use a formatter to generate output
authorMark Thomas <mbthomas@fb.com>
Fri, 12 Oct 2018 13:35:58 +0000
changeset 40187 dcda50856843
parent 40186 9cbc2579f5be
child 40188 2c5316796f45
absorb: use a formatter to generate output Change absorb to use a formatter to generate its output. This allows the use of templates to customize the output. Differential Revision: https://phab.mercurial-scm.org/D4997
hgext/absorb.py
tests/test-absorb-filefixupstate.py
--- a/hgext/absorb.py	Fri Oct 12 12:31:17 2018 -0400
+++ b/hgext/absorb.py	Fri Oct 12 13:35:58 2018 +0000
@@ -274,13 +274,14 @@
         4. read results from "finalcontents", or call getfinalcontent
     """
 
-    def __init__(self, fctxs, ui=None, opts=None):
+    def __init__(self, fctxs, path, ui=None, opts=None):
         """([fctx], ui or None) -> None
 
         fctxs should be linear, and sorted by topo order - oldest first.
         fctxs[0] will be considered as "immutable" and will not be changed.
         """
         self.fctxs = fctxs
+        self.path = path
         self.ui = ui or nullui()
         self.opts = opts or {}
 
@@ -297,7 +298,7 @@
         self.fixups = [] # [(linelog rev, a1, a2, b1, b2)]
         self.finalcontents = [] # [str]
 
-    def diffwith(self, targetfctx, showchanges=False):
+    def diffwith(self, targetfctx, fm=None):
         """calculate fixups needed by examining the differences between
         self.fctxs[-1] and targetfctx, chunk by chunk.
 
@@ -329,8 +330,8 @@
             self.chunkstats[0] += bool(newfixups) # 1 or 0
             self.chunkstats[1] += 1
             self.fixups += newfixups
-            if showchanges:
-                self._showchanges(alines, blines, chunk, newfixups)
+            if fm is not None:
+                self._showchanges(fm, alines, blines, chunk, newfixups)
 
     def apply(self):
         """apply self.fixups. update self.linelog, self.finalcontents.
@@ -546,13 +547,12 @@
         pushchunk()
         return result
 
-    def _showchanges(self, alines, blines, chunk, fixups):
-        ui = self.ui
+    def _showchanges(self, fm, alines, blines, chunk, fixups):
 
-        def label(line, label):
+        def trim(line):
             if line.endswith('\n'):
                 line = line[:-1]
-            return ui.label(line, label)
+            return line
 
         # this is not optimized for perf but _showchanges only gets executed
         # with an extra command-line flag.
@@ -564,17 +564,30 @@
             for i in pycompat.xrange(fb1, fb2):
                 bidxs[i - b1] = (max(idx, 1) - 1) // 2
 
-        buf = [] # [(idx, content)]
-        buf.append((0, label('@@ -%d,%d +%d,%d @@'
-                             % (a1, a2 - a1, b1, b2 - b1), 'diff.hunk')))
-        buf += [(aidxs[i - a1], label('-' + alines[i], 'diff.deleted'))
-                for i in pycompat.xrange(a1, a2)]
-        buf += [(bidxs[i - b1], label('+' + blines[i], 'diff.inserted'))
-                for i in pycompat.xrange(b1, b2)]
-        for idx, line in buf:
-            shortnode = idx and node.short(self.fctxs[idx].node()) or ''
-            ui.write(ui.label(shortnode[0:7].ljust(8), 'absorb.node') +
-                     line + '\n')
+        fm.startitem()
+        fm.write('hunk', '        %s\n',
+                 '@@ -%d,%d +%d,%d @@'
+                 % (a1, a2 - a1, b1, b2 - b1), label='diff.hunk')
+        fm.data(path=self.path, linetype='hunk')
+
+        def writeline(idx, diffchar, line, linetype, linelabel):
+            fm.startitem()
+            node = ''
+            if idx:
+                ctx = self.fctxs[idx]
+                fm.context(fctx=ctx)
+                node = ctx.hex()
+            fm.write('node', '%-7.7s ', node, label='absorb.node')
+            fm.write('diffchar ' + linetype, '%s%s\n', diffchar, line,
+                     label=linelabel)
+            fm.data(path=self.path, linetype=linetype)
+
+        for i in pycompat.xrange(a1, a2):
+            writeline(aidxs[i - a1], '-', trim(alines[i]), 'deleted',
+                      'diff.deleted')
+        for i in pycompat.xrange(b1, b2):
+            writeline(bidxs[i - b1], '+', trim(blines[i]), 'inserted',
+                      'diff.inserted')
 
 class fixupstate(object):
     """state needed to run absorb
@@ -609,7 +622,7 @@
         self.replacemap = {} # {oldnode: newnode or None}
         self.finalnode = None # head after all fixups
 
-    def diffwith(self, targetctx, match=None, showchanges=False):
+    def diffwith(self, targetctx, match=None, fm=None):
         """diff and prepare fixups. update self.fixupmap, self.paths"""
         # only care about modified files
         self.status = self.stack[-1].status(targetctx, match)
@@ -638,12 +651,13 @@
                 continue
             seenfctxs.update(fctxs[1:])
             self.fctxmap[path] = ctx2fctx
-            fstate = filefixupstate(fctxs, ui=self.ui, opts=self.opts)
-            if showchanges:
-                colorpath = self.ui.label(path, 'absorb.path')
-                header = 'showing changes for ' + colorpath
-                self.ui.write(header + '\n')
-            fstate.diffwith(targetfctx, showchanges=showchanges)
+            fstate = filefixupstate(fctxs, path, ui=self.ui, opts=self.opts)
+            if fm is not None:
+                fm.startitem()
+                fm.plain('showing changes for ')
+                fm.write('path', '%s\n', path, label='absorb.path')
+                fm.data(linetype='path')
+            fstate.diffwith(targetfctx, fm)
             self.fixupmap[path] = fstate
             self.paths.append(path)
 
@@ -931,7 +945,12 @@
         origchunks = patch.parsepatch(diff)
         chunks = cmdutil.recordfilter(ui, origchunks)[0]
         targetctx = overlaydiffcontext(stack[-1], chunks)
-    state.diffwith(targetctx, matcher, showchanges=opts.get('print_changes'))
+    fm = None
+    if opts.get('print_changes'):
+        fm = ui.formatter('absorb', opts)
+    state.diffwith(targetctx, matcher, fm)
+    if fm is not None:
+        fm.end()
     if not opts.get('dry_run'):
         state.apply()
         if state.commit():
@@ -948,7 +967,7 @@
           ('e', 'edit-lines', None,
            _('edit what lines belong to which changesets before commit '
              '(EXPERIMENTAL)')),
-         ] + commands.dryrunopts + commands.walkopts,
+         ] + commands.dryrunopts + commands.templateopts + commands.walkopts,
          _('hg absorb [OPTION] [FILE]...'))
 def absorbcmd(ui, repo, *pats, **opts):
     """incorporate corrections into the stack of draft changesets
--- a/tests/test-absorb-filefixupstate.py	Fri Oct 12 12:31:17 2018 -0400
+++ b/tests/test-absorb-filefixupstate.py	Fri Oct 12 13:35:58 2018 +0000
@@ -43,7 +43,8 @@
     expectedcontents = insertreturns(expectedcontents)
     oldcontents = insertreturns(oldcontents)
     workingcopy = insertreturns(workingcopy)
-    state = absorb.filefixupstate(pycompat.maplist(simplefctx, oldcontents))
+    state = absorb.filefixupstate(pycompat.maplist(simplefctx, oldcontents),
+                                  'path')
     state.diffwith(simplefctx(workingcopy))
     if fixups is not None:
         assertlistequal(state.fixups, fixups)