[PATCH v2] cmdutil: add within-line color diff capacity
matthieu.laneuville at octobus.net
matthieu.laneuville at octobus.net
Mon Nov 20 15:24:13 UTC 2017
# HG changeset patch
# User Matthieu Laneuville <matthieu.laneuville at octobus.net>
# Date 1508944418 -32400
# Thu Oct 26 00:13:38 2017 +0900
# Node ID 3f1788e4b038e5fdf9eb1c3bd4f2a7071f4db931
# Parent 75013952d8d9608f73cd45f68405fbd6ec112bf2
# EXP-Topic hg248
cmdutil: add within-line color diff capacity
The `diff' command usually writes deletion in red and insertions in green. This
patch adds within-line colors, to highlight which part of the lines differ.
The patch passes all tests except `test-diff-color.t' that had to be amended
because of the new default inline coloring.
diff -r 75013952d8d9 -r 3f1788e4b038 mercurial/cmdutil.py
--- a/mercurial/cmdutil.py Fri Nov 10 19:14:06 2017 +0800
+++ b/mercurial/cmdutil.py Thu Oct 26 00:13:38 2017 +0900
@@ -7,6 +7,7 @@
from __future__ import absolute_import
+import difflib
import errno
import itertools
import os
@@ -1513,6 +1514,11 @@ def diffordiffstat(ui, repo, diffopts, n
ui.warn(_('warning: %s not inside relative root %s\n') % (
match.uipath(matchroot), uirelroot))
+ store = {
+ 'diff.inserted': [],
+ 'diff.deleted': []
+ }
+ status = False
if stat:
diffopts = diffopts.copy(context=0)
width = 80
@@ -1529,7 +1535,56 @@ def diffordiffstat(ui, repo, diffopts, n
changes, diffopts, prefix=prefix,
relroot=relroot,
hunksfilterfn=hunksfilterfn):
- write(chunk, label=label)
+ # Each deleted/inserted chunk is followed by an EOL chunk with ''
+ # label. The 'status' flag helps us grab that second line.
+ if label in ['diff.deleted', 'diff.inserted'] or status:
+ if status:
+ store[status].append(chunk)
+ status = False
+ else:
+ store[label].append(chunk)
+ status = label
+ continue
+
+ if store['diff.inserted'] or store['diff.deleted']:
+ # It is possible that the amount of deleted/inserted lines
+ # differ, therefore we have to pad them before 1-on-1 comparison
+ while len(store['diff.deleted']) < len(store['diff.inserted']):
+ store['diff.deleted'].append(None)
+ while len(store['diff.deleted']) > len(store['diff.inserted']):
+ store['diff.inserted'].append(None)
+
+ # First print all deletions
+ for insert, delete in zip(store['diff.inserted'],
+ store['diff.deleted']):
+ if not delete:
+ continue
+ if not insert: # no matching insertion, no diff needed
+ write(delete, label='diff.deleted')
+
+ else:
+ buff = _inlinediff(insert, delete, direction='deleted')
+ for line in buff:
+ write(line[1], label=line[0])
+
+ # Then print all deletions
+ for insert, delete in zip(store['diff.inserted'],
+ store['diff.deleted']):
+ if not insert:
+ continue
+ if not delete: # no matching insertion, no diff needed
+ write(insert, label='diff.inserted')
+
+ else:
+ buff = _inlinediff(insert, delete, direction='inserted')
+ for line in buff:
+ write(line[1], label=line[0])
+
+ store['diff.inserted'] = []
+ store['diff.deleted'] = []
+
+ if chunk:
+ write(chunk, label=label)
if listsubrepos:
ctx1 = repo[node1]
@@ -1548,6 +1603,23 @@ def diffordiffstat(ui, repo, diffopts, n
sub.diff(ui, diffopts, tempnode2, submatch, changes=changes,
stat=stat, fp=fp, prefix=prefix)
+def _inlinediff(s1, s2, direction):
+ '''Perform string diff to highlight specific changes.'''
+ direction_skip = '+?' if direction == 'deleted' else '-?'
+ s = difflib.ndiff(s2.split(' '), s1.split(' '))
+ # buffer required to remove last space, there may be smarter ways to do this
+ buff = []
+ for line in s:
+ if line[0] in direction_skip:
+ continue
+ l = 'diff.' + direction + '.highlight'
+ if line[0] == ' ':
+ l = 'diff.' + direction
+ buff.append((l, line[2:] + ' '))
+
+ buff[-1] = (buff[-1][0], buff[-1][1].strip(' '))
+ return buff
+
def _changesetlabels(ctx):
labels = ['log.changeset', 'changeset.%s' % ctx.phasestr()]
if ctx.obsolete():
diff -r 75013952d8d9 -r 3f1788e4b038 mercurial/color.py
--- a/mercurial/color.py Fri Nov 10 19:14:06 2017 +0800
+++ b/mercurial/color.py Thu Oct 26 00:13:38 2017 +0900
@@ -87,12 +87,14 @@ except ImportError:
'branches.inactive': 'none',
'diff.changed': 'white',
'diff.deleted': 'red',
+ 'diff.deleted.highlight': 'red bold',
'diff.diffline': 'bold',
'diff.extended': 'cyan bold',
'diff.file_a': 'red bold',
'diff.file_b': 'green bold',
'diff.hunk': 'magenta',
'diff.inserted': 'green',
+ 'diff.inserted.highlight': 'green bold',
'diff.tab': '',
'diff.trailingwhitespace': 'bold red_background',
'changeset.public': '',
diff -r 75013952d8d9 -r 3f1788e4b038 tests/test-diff-color.t
--- a/tests/test-diff-color.t Fri Nov 10 19:14:06 2017 +0800
+++ b/tests/test-diff-color.t Thu Oct 26 00:13:38 2017 +0900
@@ -45,8 +45,8 @@ default context
c
a
a
- \x1b[0;31m-b\x1b[0m (esc)
- \x1b[0;32m+dd\x1b[0m (esc)
+ \x1b[0;31;1m-b\x1b[0m (esc)
+ \x1b[0;32;1m+dd\x1b[0m (esc)
a
a
c
@@ -93,8 +93,8 @@ default context
c
a
a
- \x1b[0;31m-b\x1b[0m (esc)
- \x1b[0;32m+dd\x1b[0m (esc)
+ \x1b[0;31;1m-b\x1b[0m (esc)
+ \x1b[0;32;1m+dd\x1b[0m (esc)
a
a
c
@@ -108,8 +108,8 @@ default context
\x1b[0;35m@@ -3,5 +3,5 @@\x1b[0m (esc)
a
a
- \x1b[0;31m-b\x1b[0m (esc)
- \x1b[0;32m+dd\x1b[0m (esc)
+ \x1b[0;31;1m-b\x1b[0m (esc)
+ \x1b[0;32;1m+dd\x1b[0m (esc)
a
a
@@ -236,11 +236,11 @@ test tabs
c
c
\x1b[0;32m+aa\x1b[0m (esc)
- \x1b[0;32m+\x1b[0m \x1b[0;32mone tab\x1b[0m (esc)
- \x1b[0;32m+\x1b[0m \x1b[0;32mtwo tabs\x1b[0m (esc)
- \x1b[0;32m+end tab\x1b[0m\x1b[0;1;41m \x1b[0m (esc)
- \x1b[0;32m+mid\x1b[0m \x1b[0;32mtab\x1b[0m (esc)
- \x1b[0;32m+\x1b[0m \x1b[0;32mall\x1b[0m \x1b[0;32mtabs\x1b[0m\x1b[0;1;41m \x1b[0m (esc)
+ \x1b[0;32m+\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mone tab\x1b[0m (esc)
+ \x1b[0;32m+\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mtwo tabs\x1b[0m (esc)
+ \x1b[0;32m+end tab\x1b[0m\x1b[0;32m \x1b[0m (esc)
+ \x1b[0;32m+mid\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mtab\x1b[0m (esc)
+ \x1b[0;32m+\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mall\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mtabs\x1b[0m\x1b[0;32m \x1b[0m (esc)
$ echo "[color]" >> $HGRCPATH
$ echo "diff.tab = bold magenta" >> $HGRCPATH
$ hg diff --nodates
@@ -252,10 +252,10 @@ test tabs
c
c
\x1b[0;32m+aa\x1b[0m (esc)
- \x1b[0;32m+\x1b[0m\x1b[0;1;35m \x1b[0m\x1b[0;32mone tab\x1b[0m (esc)
- \x1b[0;32m+\x1b[0m\x1b[0;1;35m \x1b[0m\x1b[0;32mtwo tabs\x1b[0m (esc)
- \x1b[0;32m+end tab\x1b[0m\x1b[0;1;41m \x1b[0m (esc)
- \x1b[0;32m+mid\x1b[0m\x1b[0;1;35m \x1b[0m\x1b[0;32mtab\x1b[0m (esc)
- \x1b[0;32m+\x1b[0m\x1b[0;1;35m \x1b[0m\x1b[0;32mall\x1b[0m\x1b[0;1;35m \x1b[0m\x1b[0;32mtabs\x1b[0m\x1b[0;1;41m \x1b[0m (esc)
+ \x1b[0;32m+\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mone tab\x1b[0m (esc)
+ \x1b[0;32m+\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mtwo tabs\x1b[0m (esc)
+ \x1b[0;32m+end tab\x1b[0m\x1b[0;32m \x1b[0m (esc)
+ \x1b[0;32m+mid\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mtab\x1b[0m (esc)
+ \x1b[0;32m+\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mall\x1b[0m\x1b[0;32m \x1b[0m\x1b[0;32mtabs\x1b[0m\x1b[0;32m \x1b[0m (esc)
$ cd ..
More information about the Mercurial-devel
mailing list