Show More
@@ -11,7 +11,6 b' from __future__ import absolute_import, ' | |||
|
11 | 11 | import collections |
|
12 | 12 | import contextlib |
|
13 | 13 | import copy |
|
14 | import difflib | |
|
15 | 14 | import email |
|
16 | 15 | import errno |
|
17 | 16 | import hashlib |
@@ -2481,11 +2480,32 b' def diffhunks(repo, node1=None, node2=No' | |||
|
2481 | 2480 | else: |
|
2482 | 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 | 2507 | def difflabel(func, *args, **kw): |
|
2485 | 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 | 2509 | headprefixes = [('diff', 'diff.diffline'), |
|
2490 | 2510 | ('copy', 'diff.extended'), |
|
2491 | 2511 | ('rename', 'diff.extended'), |
@@ -2497,14 +2517,20 b' def difflabel(func, *args, **kw):' | |||
|
2497 | 2517 | ('---', 'diff.file_a'), |
|
2498 | 2518 | ('+++', 'diff.file_b')] |
|
2499 | 2519 | textprefixes = [('@', 'diff.hunk'), |
|
2500 | ('-', 'diff.deleted'), | |
|
2501 |
|
|
|
2520 | # - and + are handled by diffsinglehunk | |
|
2521 | ] | |
|
2502 | 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 | 2532 | for chunk in func(*args, **kw): |
|
2504 | 2533 | lines = chunk.split('\n') |
|
2505 | matches = {} | |
|
2506 | if inlinecolor: | |
|
2507 | matches = _findmatches(lines) | |
|
2508 | 2534 | linecount = len(lines) |
|
2509 | 2535 | for i, line in enumerate(lines): |
|
2510 | 2536 | if head: |
@@ -2513,109 +2539,37 b' def difflabel(func, *args, **kw):' | |||
|
2513 | 2539 | else: |
|
2514 | 2540 | if line and not line.startswith((' ', '+', '-', '@', '\\')): |
|
2515 | 2541 | head = True |
|
2516 | stripline = line | |
|
2517 | 2542 | diffline = False |
|
2518 | 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 | 2544 | diffline = True |
|
2523 | 2545 | |
|
2524 | 2546 | prefixes = textprefixes |
|
2525 | 2547 | if head: |
|
2526 | 2548 | prefixes = headprefixes |
|
2527 |
f |
|
|
2528 | if stripline.startswith(prefix): | |
|
2529 |
|
|
|
2530 |
|
|
|
2531 | for t, l in _inlinediff(lines[i].rstrip(), | |
|
2532 | lines[matches[i]].rstrip(), | |
|
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 | |
|
2549 | if diffline: | |
|
2550 | # buffered | |
|
2551 | bufferedline = line | |
|
2552 | if i + 1 < linecount: | |
|
2553 | bufferedline += "\n" | |
|
2554 | hunkbuffer.append(bufferedline) | |
|
2544 | 2555 | else: |
|
2545 |
|
|
|
2546 | if line != stripline: | |
|
2547 | yield (line[len(stripline):], 'diff.trailingwhitespace') | |
|
2548 | if i + 1 < linecount: | |
|
2549 | yield ('\n', '') | |
|
2550 | ||
|
2551 | def _findmatches(slist): | |
|
2552 | '''Look for insertion matches to deletion and returns a dict of | |
|
2553 | correspondences. | |
|
2554 | ''' | |
|
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 | |
|
2556 | # unbuffered | |
|
2557 | for token in consumehunkbuffer(): | |
|
2558 | yield token | |
|
2559 | stripline = line.rstrip() | |
|
2560 | for prefix, label in prefixes: | |
|
2561 | if stripline.startswith(prefix): | |
|
2562 | yield (stripline, label) | |
|
2563 | if line != stripline: | |
|
2564 | yield (line[len(stripline):], | |
|
2565 | 'diff.trailingwhitespace') | |
|
2575 | 2566 | break |
|
2576 | return matches | |
|
2577 | ||
|
2578 | def _inlinediff(s1, s2, operation): | |
|
2579 | '''Perform string diff to highlight specific changes.''' | |
|
2580 | operation_skip = ('+', '?') if operation == 'diff.deleted' else ('-', '?') | |
|
2581 | if operation == 'diff.deleted': | |
|
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 | |
|
2567 | else: | |
|
2568 | yield (line, '') | |
|
2569 | if i + 1 < linecount: | |
|
2570 | yield ('\n', '') | |
|
2571 | for token in consumehunkbuffer(): | |
|
2572 | yield token | |
|
2619 | 2573 | |
|
2620 | 2574 | def diffui(*args, **kw): |
|
2621 | 2575 | '''like diff(), but yields 2-tuples of (output, label) for ui.write()''' |
@@ -337,6 +337,7 b' test inline color diff' | |||
|
337 | 337 | [diff.deleted|-(to see if it works)] |
|
338 | 338 | [diff.inserted|+three of those lines have] |
|
339 | 339 | [diff.inserted|+collapsed onto one] |
|
340 | #if false | |
|
340 | 341 | $ hg diff --config experimental.worddiff=True --color=debug |
|
341 | 342 | [diff.diffline|diff --git a/file1 b/file1] |
|
342 | 343 | [diff.file_a|--- a/file1] |
@@ -370,6 +371,7 b' test inline color diff' | |||
|
370 | 371 | [diff.deleted|-(to see if it works)] |
|
371 | 372 | [diff.inserted|+three of those lines ][diff.inserted.highlight|have] |
|
372 | 373 | [diff.inserted|+][diff.inserted.highlight|collapsed][diff.inserted| onto one] |
|
374 | #endif | |
|
373 | 375 | |
|
374 | 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 | 385 | > f.write(b"blah \xe3\x82\xa4 blah\n") |
|
384 | 386 | > EOF |
|
385 | 387 | $ hg ci -m 'slightly change utf8 char' utf8 |
|
388 | ||
|
389 | #if false | |
|
386 | 390 | $ hg diff --config experimental.worddiff=True --color=debug -c. |
|
387 | 391 | [diff.diffline|diff --git a/utf8 b/utf8] |
|
388 | 392 | [diff.file_a|--- a/utf8] |
@@ -390,3 +394,4 b" multibyte character shouldn't be broken " | |||
|
390 | 394 | [diff.hunk|@@ -1,1 +1,1 @@] |
|
391 | 395 | [diff.deleted|-blah ][diff.deleted.highlight|\xe3\x82\xa2][diff.deleted| blah] (esc) |
|
392 | 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