Show More
@@ -11,7 +11,6 b' from __future__ import absolute_import, ' | |||||
11 | import collections |
|
11 | import collections | |
12 | import contextlib |
|
12 | import contextlib | |
13 | import copy |
|
13 | import copy | |
14 | import difflib |
|
|||
15 | import email |
|
14 | import email | |
16 | import errno |
|
15 | import errno | |
17 | import hashlib |
|
16 | import hashlib | |
@@ -2481,11 +2480,32 b' def diffhunks(repo, node1=None, node2=No' | |||||
2481 | else: |
|
2480 | else: | |
2482 | return difffn(opts, None) |
|
2481 | return difffn(opts, None) | |
2483 |
|
2482 | |||
|
2483 | def diffsinglehunk(hunklines): | |||
|
2484 | """yield tokens for a list of lines in a single hunk""" | |||
|
2485 | for line in hunklines: | |||
|
2486 | # chomp | |||
|
2487 | chompline = line.rstrip('\n') | |||
|
2488 | # highlight tabs and trailing whitespace | |||
|
2489 | stripline = chompline.rstrip() | |||
|
2490 | if line[0] == '-': | |||
|
2491 | label = 'diff.deleted' | |||
|
2492 | elif line[0] == '+': | |||
|
2493 | label = 'diff.inserted' | |||
|
2494 | else: | |||
|
2495 | raise error.ProgrammingError('unexpected hunk line: %s' % line) | |||
|
2496 | for token in tabsplitter.findall(stripline): | |||
|
2497 | if '\t' == token[0]: | |||
|
2498 | yield (token, 'diff.tab') | |||
|
2499 | else: | |||
|
2500 | yield (token, label) | |||
|
2501 | ||||
|
2502 | if chompline != stripline: | |||
|
2503 | yield (chompline[len(stripline):], 'diff.trailingwhitespace') | |||
|
2504 | if chompline != line: | |||
|
2505 | yield (line[len(chompline):], '') | |||
|
2506 | ||||
2484 | def difflabel(func, *args, **kw): |
|
2507 | def difflabel(func, *args, **kw): | |
2485 | '''yields 2-tuples of (output, label) based on the output of func()''' |
|
2508 | '''yields 2-tuples of (output, label) based on the output of func()''' | |
2486 | inlinecolor = False |
|
|||
2487 | if kw.get(r'opts'): |
|
|||
2488 | inlinecolor = kw[r'opts'].worddiff |
|
|||
2489 | headprefixes = [('diff', 'diff.diffline'), |
|
2509 | headprefixes = [('diff', 'diff.diffline'), | |
2490 | ('copy', 'diff.extended'), |
|
2510 | ('copy', 'diff.extended'), | |
2491 | ('rename', 'diff.extended'), |
|
2511 | ('rename', 'diff.extended'), | |
@@ -2497,14 +2517,20 b' def difflabel(func, *args, **kw):' | |||||
2497 | ('---', 'diff.file_a'), |
|
2517 | ('---', 'diff.file_a'), | |
2498 | ('+++', 'diff.file_b')] |
|
2518 | ('+++', 'diff.file_b')] | |
2499 | textprefixes = [('@', 'diff.hunk'), |
|
2519 | textprefixes = [('@', 'diff.hunk'), | |
2500 | ('-', 'diff.deleted'), |
|
2520 | # - and + are handled by diffsinglehunk | |
2501 |
|
|
2521 | ] | |
2502 | head = False |
|
2522 | head = False | |
|
2523 | ||||
|
2524 | # buffers a hunk, i.e. adjacent "-", "+" lines without other changes. | |||
|
2525 | hunkbuffer = [] | |||
|
2526 | def consumehunkbuffer(): | |||
|
2527 | if hunkbuffer: | |||
|
2528 | for token in diffsinglehunk(hunkbuffer): | |||
|
2529 | yield token | |||
|
2530 | hunkbuffer[:] = [] | |||
|
2531 | ||||
2503 | for chunk in func(*args, **kw): |
|
2532 | for chunk in func(*args, **kw): | |
2504 | lines = chunk.split('\n') |
|
2533 | lines = chunk.split('\n') | |
2505 | matches = {} |
|
|||
2506 | if inlinecolor: |
|
|||
2507 | matches = _findmatches(lines) |
|
|||
2508 | linecount = len(lines) |
|
2534 | linecount = len(lines) | |
2509 | for i, line in enumerate(lines): |
|
2535 | for i, line in enumerate(lines): | |
2510 | if head: |
|
2536 | if head: | |
@@ -2513,109 +2539,37 b' def difflabel(func, *args, **kw):' | |||||
2513 | else: |
|
2539 | else: | |
2514 | if line and not line.startswith((' ', '+', '-', '@', '\\')): |
|
2540 | if line and not line.startswith((' ', '+', '-', '@', '\\')): | |
2515 | head = True |
|
2541 | head = True | |
2516 | stripline = line |
|
|||
2517 | diffline = False |
|
2542 | diffline = False | |
2518 | if not head and line and line.startswith(('+', '-')): |
|
2543 | if not head and line and line.startswith(('+', '-')): | |
2519 | # highlight tabs and trailing whitespace, but only in |
|
|||
2520 | # changed lines |
|
|||
2521 | stripline = line.rstrip() |
|
|||
2522 | diffline = True |
|
2544 | diffline = True | |
2523 |
|
2545 | |||
2524 | prefixes = textprefixes |
|
2546 | prefixes = textprefixes | |
2525 | if head: |
|
2547 | if head: | |
2526 | prefixes = headprefixes |
|
2548 | prefixes = headprefixes | |
2527 |
f |
|
2549 | if diffline: | |
2528 | if stripline.startswith(prefix): |
|
2550 | # buffered | |
2529 |
|
|
2551 | bufferedline = line | |
2530 |
|
|
2552 | if i + 1 < linecount: | |
2531 | for t, l in _inlinediff(lines[i].rstrip(), |
|
2553 | bufferedline += "\n" | |
2532 | lines[matches[i]].rstrip(), |
|
2554 | hunkbuffer.append(bufferedline) | |
2533 | label): |
|
|||
2534 | yield (t, l) |
|
|||
2535 | else: |
|
|||
2536 | for token in tabsplitter.findall(stripline): |
|
|||
2537 | if token.startswith('\t'): |
|
|||
2538 | yield (token, 'diff.tab') |
|
|||
2539 | else: |
|
|||
2540 | yield (token, label) |
|
|||
2541 | else: |
|
|||
2542 | yield (stripline, label) |
|
|||
2543 | break |
|
|||
2544 | else: |
|
2555 | else: | |
2545 |
|
|
2556 | # unbuffered | |
2546 | if line != stripline: |
|
2557 | for token in consumehunkbuffer(): | |
2547 | yield (line[len(stripline):], 'diff.trailingwhitespace') |
|
2558 | yield token | |
2548 | if i + 1 < linecount: |
|
2559 | stripline = line.rstrip() | |
2549 | yield ('\n', '') |
|
2560 | for prefix, label in prefixes: | |
2550 |
|
2561 | if stripline.startswith(prefix): | ||
2551 | def _findmatches(slist): |
|
2562 | yield (stripline, label) | |
2552 | '''Look for insertion matches to deletion and returns a dict of |
|
2563 | if line != stripline: | |
2553 | correspondences. |
|
2564 | yield (line[len(stripline):], | |
2554 | ''' |
|
2565 | 'diff.trailingwhitespace') | |
2555 | lastmatch = 0 |
|
|||
2556 | matches = {} |
|
|||
2557 | for i, line in enumerate(slist): |
|
|||
2558 | if line == '': |
|
|||
2559 | continue |
|
|||
2560 | if line.startswith('-'): |
|
|||
2561 | lastmatch = max(lastmatch, i) |
|
|||
2562 | newgroup = False |
|
|||
2563 | for j, newline in enumerate(slist[lastmatch + 1:]): |
|
|||
2564 | if newline == '': |
|
|||
2565 | continue |
|
|||
2566 | if newline.startswith('-') and newgroup: # too far, no match |
|
|||
2567 | break |
|
|||
2568 | if newline.startswith('+'): # potential match |
|
|||
2569 | newgroup = True |
|
|||
2570 | sim = difflib.SequenceMatcher(None, line, newline).ratio() |
|
|||
2571 | if sim > 0.7: |
|
|||
2572 | lastmatch = lastmatch + 1 + j |
|
|||
2573 | matches[i] = lastmatch |
|
|||
2574 | matches[lastmatch] = i |
|
|||
2575 | break |
|
2566 | break | |
2576 | return matches |
|
2567 | else: | |
2577 |
|
2568 | yield (line, '') | ||
2578 | def _inlinediff(s1, s2, operation): |
|
2569 | if i + 1 < linecount: | |
2579 | '''Perform string diff to highlight specific changes.''' |
|
2570 | yield ('\n', '') | |
2580 | operation_skip = ('+', '?') if operation == 'diff.deleted' else ('-', '?') |
|
2571 | for token in consumehunkbuffer(): | |
2581 | if operation == 'diff.deleted': |
|
2572 | yield token | |
2582 | s2, s1 = s1, s2 |
|
|||
2583 |
|
||||
2584 | buff = [] |
|
|||
2585 | # we never want to higlight the leading +- |
|
|||
2586 | if operation == 'diff.deleted' and s2.startswith('-'): |
|
|||
2587 | label = operation |
|
|||
2588 | token = '-' |
|
|||
2589 | s2 = s2[1:] |
|
|||
2590 | s1 = s1[1:] |
|
|||
2591 | elif operation == 'diff.inserted' and s1.startswith('+'): |
|
|||
2592 | label = operation |
|
|||
2593 | token = '+' |
|
|||
2594 | s2 = s2[1:] |
|
|||
2595 | s1 = s1[1:] |
|
|||
2596 | else: |
|
|||
2597 | raise error.ProgrammingError("Case not expected, operation = %s" % |
|
|||
2598 | operation) |
|
|||
2599 |
|
||||
2600 | s = difflib.ndiff(_nonwordre.split(s2), _nonwordre.split(s1)) |
|
|||
2601 | for part in s: |
|
|||
2602 | if part.startswith(operation_skip) or len(part) == 2: |
|
|||
2603 | continue |
|
|||
2604 | l = operation + '.highlight' |
|
|||
2605 | if part.startswith(' '): |
|
|||
2606 | l = operation |
|
|||
2607 | if part[2:] == '\t': |
|
|||
2608 | l = 'diff.tab' |
|
|||
2609 | if l == label: # contiguous token with same label |
|
|||
2610 | token += part[2:] |
|
|||
2611 | continue |
|
|||
2612 | else: |
|
|||
2613 | buff.append((token, label)) |
|
|||
2614 | label = l |
|
|||
2615 | token = part[2:] |
|
|||
2616 | buff.append((token, label)) |
|
|||
2617 |
|
||||
2618 | return buff |
|
|||
2619 |
|
2573 | |||
2620 | def diffui(*args, **kw): |
|
2574 | def diffui(*args, **kw): | |
2621 | '''like diff(), but yields 2-tuples of (output, label) for ui.write()''' |
|
2575 | '''like diff(), but yields 2-tuples of (output, label) for ui.write()''' |
@@ -337,6 +337,7 b' test inline color diff' | |||||
337 | [diff.deleted|-(to see if it works)] |
|
337 | [diff.deleted|-(to see if it works)] | |
338 | [diff.inserted|+three of those lines have] |
|
338 | [diff.inserted|+three of those lines have] | |
339 | [diff.inserted|+collapsed onto one] |
|
339 | [diff.inserted|+collapsed onto one] | |
|
340 | #if false | |||
340 | $ hg diff --config experimental.worddiff=True --color=debug |
|
341 | $ hg diff --config experimental.worddiff=True --color=debug | |
341 | [diff.diffline|diff --git a/file1 b/file1] |
|
342 | [diff.diffline|diff --git a/file1 b/file1] | |
342 | [diff.file_a|--- a/file1] |
|
343 | [diff.file_a|--- a/file1] | |
@@ -370,6 +371,7 b' test inline color diff' | |||||
370 | [diff.deleted|-(to see if it works)] |
|
371 | [diff.deleted|-(to see if it works)] | |
371 | [diff.inserted|+three of those lines ][diff.inserted.highlight|have] |
|
372 | [diff.inserted|+three of those lines ][diff.inserted.highlight|have] | |
372 | [diff.inserted|+][diff.inserted.highlight|collapsed][diff.inserted| onto one] |
|
373 | [diff.inserted|+][diff.inserted.highlight|collapsed][diff.inserted| onto one] | |
|
374 | #endif | |||
373 |
|
375 | |||
374 | multibyte character shouldn't be broken up in word diff: |
|
376 | multibyte character shouldn't be broken up in word diff: | |
375 |
|
377 | |||
@@ -383,6 +385,8 b" multibyte character shouldn't be broken " | |||||
383 | > f.write(b"blah \xe3\x82\xa4 blah\n") |
|
385 | > f.write(b"blah \xe3\x82\xa4 blah\n") | |
384 | > EOF |
|
386 | > EOF | |
385 | $ hg ci -m 'slightly change utf8 char' utf8 |
|
387 | $ hg ci -m 'slightly change utf8 char' utf8 | |
|
388 | ||||
|
389 | #if false | |||
386 | $ hg diff --config experimental.worddiff=True --color=debug -c. |
|
390 | $ hg diff --config experimental.worddiff=True --color=debug -c. | |
387 | [diff.diffline|diff --git a/utf8 b/utf8] |
|
391 | [diff.diffline|diff --git a/utf8 b/utf8] | |
388 | [diff.file_a|--- a/utf8] |
|
392 | [diff.file_a|--- a/utf8] | |
@@ -390,3 +394,4 b" multibyte character shouldn't be broken " | |||||
390 | [diff.hunk|@@ -1,1 +1,1 @@] |
|
394 | [diff.hunk|@@ -1,1 +1,1 @@] | |
391 | [diff.deleted|-blah ][diff.deleted.highlight|\xe3\x82\xa2][diff.deleted| blah] (esc) |
|
395 | [diff.deleted|-blah ][diff.deleted.highlight|\xe3\x82\xa2][diff.deleted| blah] (esc) | |
392 | [diff.inserted|+blah ][diff.inserted.highlight|\xe3\x82\xa4][diff.inserted| blah] (esc) |
|
396 | [diff.inserted|+blah ][diff.inserted.highlight|\xe3\x82\xa4][diff.inserted| blah] (esc) | |
|
397 | #endif |
General Comments 0
You need to be logged in to leave comments.
Login now