##// END OF EJS Templates
patch: add within-line color diff capacity...
Matthieu Laneuville -
r35278:6ba79cf3 default
parent child Browse files
Show More
@@ -1518,7 +1518,7 b' def diffordiffstat(ui, repo, diffopts, n'
1518 1518 width = 80
1519 1519 if not ui.plain():
1520 1520 width = ui.termwidth()
1521 chunks = patch.diff(repo, node1, node2, match, changes, diffopts,
1521 chunks = patch.diff(repo, node1, node2, match, changes, opts=diffopts,
1522 1522 prefix=prefix, relroot=relroot,
1523 1523 hunksfilterfn=hunksfilterfn)
1524 1524 for chunk, label in patch.diffstatui(util.iterlines(chunks),
@@ -1526,7 +1526,7 b' def diffordiffstat(ui, repo, diffopts, n'
1526 1526 write(chunk, label=label)
1527 1527 else:
1528 1528 for chunk, label in patch.diffui(repo, node1, node2, match,
1529 changes, diffopts, prefix=prefix,
1529 changes, opts=diffopts, prefix=prefix,
1530 1530 relroot=relroot,
1531 1531 hunksfilterfn=hunksfilterfn):
1532 1532 write(chunk, label=label)
@@ -87,12 +87,14 b' except ImportError:'
87 87 'branches.inactive': 'none',
88 88 'diff.changed': 'white',
89 89 'diff.deleted': 'red',
90 'diff.deleted.highlight': 'red bold underline',
90 91 'diff.diffline': 'bold',
91 92 'diff.extended': 'cyan bold',
92 93 'diff.file_a': 'red bold',
93 94 'diff.file_b': 'green bold',
94 95 'diff.hunk': 'magenta',
95 96 'diff.inserted': 'green',
97 'diff.inserted.highlight': 'green bold underline',
96 98 'diff.tab': '',
97 99 'diff.trailingwhitespace': 'bold red_background',
98 100 'changeset.public': '',
@@ -481,6 +481,9 b" coreconfigitem('experimental', 'evolutio"
481 481 coreconfigitem('experimental', 'evolution.track-operation',
482 482 default=True,
483 483 )
484 coreconfigitem('experimental', 'worddiff',
485 default=False,
486 )
484 487 coreconfigitem('experimental', 'maxdeltachainspan',
485 488 default=-1,
486 489 )
@@ -67,6 +67,7 b' class diffopts(object):'
67 67 'ignoreblanklines': False,
68 68 'upgrade': False,
69 69 'showsimilarity': False,
70 'worddiff': False,
70 71 }
71 72
72 73 def __init__(self, **opts):
@@ -10,6 +10,7 b' from __future__ import absolute_import, '
10 10
11 11 import collections
12 12 import copy
13 import difflib
13 14 import email
14 15 import errno
15 16 import hashlib
@@ -2252,6 +2253,7 b' def difffeatureopts(ui, opts=None, untru'
2252 2253 'showfunc': get('show_function', 'showfunc'),
2253 2254 'context': get('unified', getter=ui.config),
2254 2255 }
2256 buildopts['worddiff'] = ui.configbool('experimental', 'worddiff')
2255 2257
2256 2258 if git:
2257 2259 buildopts['git'] = get('git')
@@ -2463,6 +2465,9 b' def diffhunks(repo, node1=None, node2=No'
2463 2465
2464 2466 def difflabel(func, *args, **kw):
2465 2467 '''yields 2-tuples of (output, label) based on the output of func()'''
2468 inlinecolor = False
2469 if kw.get('opts'):
2470 inlinecolor = kw['opts'].worddiff
2466 2471 headprefixes = [('diff', 'diff.diffline'),
2467 2472 ('copy', 'diff.extended'),
2468 2473 ('rename', 'diff.extended'),
@@ -2479,6 +2484,9 b' def difflabel(func, *args, **kw):'
2479 2484 head = False
2480 2485 for chunk in func(*args, **kw):
2481 2486 lines = chunk.split('\n')
2487 matches = {}
2488 if inlinecolor:
2489 matches = _findmatches(lines)
2482 2490 for i, line in enumerate(lines):
2483 2491 if i != 0:
2484 2492 yield ('\n', '')
@@ -2506,7 +2514,14 b' def difflabel(func, *args, **kw):'
2506 2514 if '\t' == token[0]:
2507 2515 yield (token, 'diff.tab')
2508 2516 else:
2509 yield (token, label)
2517 if i in matches:
2518 for l, t in _inlinediff(
2519 lines[i].rstrip(),
2520 lines[matches[i]].rstrip(),
2521 label):
2522 yield (t, l)
2523 else:
2524 yield (token, label)
2510 2525 else:
2511 2526 yield (stripline, label)
2512 2527 break
@@ -2515,6 +2530,70 b' def difflabel(func, *args, **kw):'
2515 2530 if line != stripline:
2516 2531 yield (line[len(stripline):], 'diff.trailingwhitespace')
2517 2532
2533 def _findmatches(slist):
2534 '''Look for insertion matches to deletion and returns a dict of
2535 correspondences.
2536 '''
2537 lastmatch = 0
2538 matches = {}
2539 for i, line in enumerate(slist):
2540 if line == '':
2541 continue
2542 if line[0] == '-':
2543 lastmatch = max(lastmatch, i)
2544 newgroup = False
2545 for j, newline in enumerate(slist[lastmatch + 1:]):
2546 if newline == '':
2547 continue
2548 if newline[0] == '-' and newgroup: # too far, no match
2549 break
2550 if newline[0] == '+': # potential match
2551 newgroup = True
2552 sim = difflib.SequenceMatcher(None, line, newline).ratio()
2553 if sim > 0.7:
2554 lastmatch = lastmatch + 1 + j
2555 matches[i] = lastmatch
2556 matches[lastmatch] = i
2557 break
2558 return matches
2559
2560 def _inlinediff(s1, s2, operation):
2561 '''Perform string diff to highlight specific changes.'''
2562 operation_skip = '+?' if operation == 'diff.deleted' else '-?'
2563 if operation == 'diff.deleted':
2564 s2, s1 = s1, s2
2565
2566 buff = []
2567 # we never want to higlight the leading +-
2568 if operation == 'diff.deleted' and s2.startswith('-'):
2569 label = operation
2570 token = '-'
2571 s2 = s2[1:]
2572 s1 = s1[1:]
2573 elif operation == 'diff.inserted' and s1.startswith('+'):
2574 label = operation
2575 token = '+'
2576 s2 = s2[1:]
2577 s1 = s1[1:]
2578
2579 s = difflib.ndiff(re.split(br'(\W)', s2), re.split(br'(\W)', s1))
2580 for part in s:
2581 if part[0] in operation_skip:
2582 continue
2583 l = operation + '.highlight'
2584 if part[0] in ' ':
2585 l = operation
2586 if l == label: # contiguous token with same label
2587 token += part[2:]
2588 continue
2589 else:
2590 buff.append((label, token))
2591 label = l
2592 token = part[2:]
2593 buff.append((label, token))
2594
2595 return buff
2596
2518 2597 def diffui(*args, **kw):
2519 2598 '''like diff(), but yields 2-tuples of (output, label) for ui.write()'''
2520 2599 return difflabel(diff, *args, **kw)
@@ -259,3 +259,95 b' test tabs'
259 259 \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)
260 260
261 261 $ cd ..
262
263 test inline color diff
264
265 $ hg init inline
266 $ cd inline
267 $ cat > file1 << EOF
268 > this is the first line
269 > this is the second line
270 > third line starts with space
271 > + starts with a plus sign
272 >
273 > this line won't change
274 >
275 > two lines are going to
276 > be changed into three!
277 >
278 > three of those lines will
279 > collapse onto one
280 > (to see if it works)
281 > EOF
282 $ hg add file1
283 $ hg ci -m 'commit'
284 $ cat > file1 << EOF
285 > that is the first paragraph
286 > this is the second line
287 > third line starts with space
288 > - starts with a minus sign
289 >
290 > this line won't change
291 >
292 > two lines are going to
293 > (entirely magically,
294 > assuming this works)
295 > be changed into four!
296 >
297 > three of those lines have
298 > collapsed onto one
299 > EOF
300 $ hg diff --config experimental.worddiff=False --color=debug
301 [diff.diffline|diff --git a/file1 b/file1]
302 [diff.file_a|--- a/file1]
303 [diff.file_b|+++ b/file1]
304 [diff.hunk|@@ -1,13 +1,14 @@]
305 [diff.deleted|-this is the first line]
306 [diff.deleted|-this is the second line]
307 [diff.deleted|- third line starts with space]
308 [diff.deleted|-+ starts with a plus sign]
309 [diff.inserted|+that is the first paragraph]
310 [diff.inserted|+ this is the second line]
311 [diff.inserted|+third line starts with space]
312 [diff.inserted|+- starts with a minus sign]
313
314 this line won't change
315
316 two lines are going to
317 [diff.deleted|-be changed into three!]
318 [diff.inserted|+(entirely magically,]
319 [diff.inserted|+ assuming this works)]
320 [diff.inserted|+be changed into four!]
321
322 [diff.deleted|-three of those lines will]
323 [diff.deleted|-collapse onto one]
324 [diff.deleted|-(to see if it works)]
325 [diff.inserted|+three of those lines have]
326 [diff.inserted|+collapsed onto one]
327 $ hg diff --config experimental.worddiff=True --color=debug
328 [diff.diffline|diff --git a/file1 b/file1]
329 [diff.file_a|--- a/file1]
330 [diff.file_b|+++ b/file1]
331 [diff.hunk|@@ -1,13 +1,14 @@]
332 [diff.deleted|-this is the ][diff.deleted.highlight|first][diff.deleted| line]
333 [diff.deleted|-this is the second line]
334 [diff.deleted|-][diff.deleted.highlight| ][diff.deleted|third line starts with space]
335 [diff.deleted|-][diff.deleted.highlight|+][diff.deleted| starts with a ][diff.deleted.highlight|plus][diff.deleted| sign]
336 [diff.inserted|+that is the first paragraph]
337 [diff.inserted|+][diff.inserted.highlight| ][diff.inserted|this is the ][diff.inserted.highlight|second][diff.inserted| line]
338 [diff.inserted|+third line starts with space]
339 [diff.inserted|+][diff.inserted.highlight|-][diff.inserted| starts with a ][diff.inserted.highlight|minus][diff.inserted| sign]
340
341 this line won't change
342
343 two lines are going to
344 [diff.deleted|-be changed into ][diff.deleted.highlight|three][diff.deleted|!]
345 [diff.inserted|+(entirely magically,]
346 [diff.inserted|+ assuming this works)]
347 [diff.inserted|+be changed into ][diff.inserted.highlight|four][diff.inserted|!]
348
349 [diff.deleted|-three of those lines ][diff.deleted.highlight|will]
350 [diff.deleted|-][diff.deleted.highlight|collapse][diff.deleted| onto one]
351 [diff.deleted|-(to see if it works)]
352 [diff.inserted|+three of those lines ][diff.inserted.highlight|have]
353 [diff.inserted|+][diff.inserted.highlight|collapsed][diff.inserted| onto one]
General Comments 0
You need to be logged in to leave comments. Login now