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 |
|
|
|
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