##// 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 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 ('+', 'diff.inserted')]
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 for prefix, label in prefixes:
2528 if stripline.startswith(prefix):
2529 if diffline:
2530 if i in matches:
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 yield (line, '')
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