##// END OF EJS Templates
patch: buffer lines for a same hunk...
Jun Wu -
r37749:54713489 default
parent child Browse files
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 ('+', 'diff.inserted')]
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 for prefix, label in prefixes:
2549 if diffline:
2528 if stripline.startswith(prefix):
2550 # buffered
2529 if diffline:
2551 bufferedline = line
2530 if i in matches:
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 yield (line, '')
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