|
@@ -384,14 +384,53 @@ class Udiff(Diff):
|
384
|
384
|
return line.startswith(r'\ No newline at end of')
|
385
|
385
|
|
386
|
386
|
|
|
387
|
+class PatchStream(object):
|
|
388
|
+
|
|
389
|
+ def __init__(self, diff_hdl):
|
|
390
|
+ self._diff_hdl = diff_hdl
|
|
391
|
+ self._header_chunk_size = 0
|
|
392
|
+ self._header_chunk = []
|
|
393
|
+
|
|
394
|
+ # Test whether stream is empty by read 1 line
|
|
395
|
+ line = self._diff_hdl.readline()
|
|
396
|
+ if line is None:
|
|
397
|
+ self._is_empty = True
|
|
398
|
+ else:
|
|
399
|
+ self._header_chunk.append(line)
|
|
400
|
+ self._header_chunk_size += 1
|
|
401
|
+ self._is_empty = False
|
|
402
|
+
|
|
403
|
+ def is_empty(self):
|
|
404
|
+ return self._is_empty
|
|
405
|
+
|
|
406
|
+ def read_header_chunks(self, header_chunk_size):
|
|
407
|
+ """Returns a small chunk for patch type detect, suppose to call once"""
|
|
408
|
+ for i in range(1, header_chunk_size):
|
|
409
|
+ line = self._diff_hdl.readline()
|
|
410
|
+ if line is None:
|
|
411
|
+ break
|
|
412
|
+ self._header_chunk.append(line)
|
|
413
|
+ self._header_chunk_size += 1
|
|
414
|
+ yield line
|
|
415
|
+
|
|
416
|
+ def __iter__(self):
|
|
417
|
+ for line in self._header_chunk:
|
|
418
|
+ yield line
|
|
419
|
+ for line in self._diff_hdl:
|
|
420
|
+ yield line
|
|
421
|
+
|
|
422
|
+
|
387
|
423
|
class DiffParser(object):
|
388
|
424
|
|
389
|
425
|
def __init__(self, stream):
|
390
|
426
|
"""Detect Udiff with 3 conditions, '## ' uaually indicates svn property
|
391
|
427
|
changes in output from `svn log --diff`
|
392
|
428
|
"""
|
|
429
|
+ self._stream = stream
|
|
430
|
+
|
393
|
431
|
flag = 0
|
394
|
|
- for line in stream[:100]:
|
|
432
|
+ for line in self._stream.read_header_chunks(100):
|
|
433
|
+ line = decode(line)
|
395
|
434
|
if line.startswith('--- '):
|
396
|
435
|
flag |= 1
|
397
|
436
|
elif line.startswith('+++ '):
|
|
@@ -404,74 +443,73 @@ class DiffParser(object):
|
404
|
443
|
else:
|
405
|
444
|
raise RuntimeError('unknown diff type')
|
406
|
445
|
|
|
446
|
+ def get_diff_generator(self):
|
407
|
447
|
try:
|
408
|
|
- self._diffs = self._parse(stream)
|
|
448
|
+ return self._parse()
|
409
|
449
|
except (AssertionError, IndexError):
|
410
|
450
|
raise RuntimeError('invalid patch format')
|
411
|
451
|
|
412
|
|
- def get_diffs(self):
|
413
|
|
- return self._diffs
|
414
|
|
-
|
415
|
|
- def _parse(self, stream):
|
|
452
|
+ def _parse(self):
|
416
|
453
|
"""parse all diff lines, construct a list of Diff objects"""
|
417
|
454
|
if self._type == 'udiff':
|
418
|
455
|
difflet = Udiff(None, None, None, None)
|
419
|
456
|
else:
|
420
|
457
|
raise RuntimeError('unsupported diff format')
|
421
|
458
|
|
422
|
|
- out_diffs = []
|
|
459
|
+ diff = Diff([], None, None, [])
|
423
|
460
|
headers = []
|
424
|
461
|
|
425
|
|
- while stream:
|
426
|
|
- if difflet.is_old_path(stream[0]):
|
427
|
|
- old_path = stream.pop(0)
|
428
|
|
- out_diffs.append(Diff(headers, old_path, None, []))
|
|
462
|
+ for line in self._stream:
|
|
463
|
+ line = decode(line)
|
|
464
|
+
|
|
465
|
+ if difflet.is_old_path(line):
|
|
466
|
+ if diff._old_path and diff._new_path and len(diff._hunks) > 0:
|
|
467
|
+ # One diff constructed
|
|
468
|
+ yield diff
|
|
469
|
+ diff = Diff([], None, None, [])
|
|
470
|
+ diff = Diff(headers, line, None, [])
|
429
|
471
|
headers = []
|
430
|
472
|
|
431
|
|
- elif difflet.is_new_path(stream[0]):
|
432
|
|
- new_path = stream.pop(0)
|
433
|
|
- out_diffs[-1]._new_path = new_path
|
|
473
|
+ elif difflet.is_new_path(line):
|
|
474
|
+ diff._new_path = line
|
434
|
475
|
|
435
|
|
- elif difflet.is_hunk_meta(stream[0]):
|
436
|
|
- hunk_meta = stream.pop(0)
|
|
476
|
+ elif difflet.is_hunk_meta(line):
|
|
477
|
+ hunk_meta = line
|
437
|
478
|
old_addr, new_addr = difflet.parse_hunk_meta(hunk_meta)
|
438
|
479
|
hunk = Hunk(headers, hunk_meta, old_addr, new_addr)
|
439
|
480
|
headers = []
|
440
|
|
- out_diffs[-1]._hunks.append(hunk)
|
|
481
|
+ diff._hunks.append(hunk)
|
441
|
482
|
|
442
|
|
- elif out_diffs and out_diffs[-1]._hunks and \
|
443
|
|
- (difflet.is_old(stream[0]) or difflet.is_new(stream[0]) or \
|
444
|
|
- difflet.is_common(stream[0])):
|
445
|
|
- hunk_line = stream.pop(0)
|
446
|
|
- out_diffs[-1]._hunks[-1].append(hunk_line[0], hunk_line[1:])
|
|
483
|
+ elif len(diff._hunks) > 0 and (difflet.is_old(line) or \
|
|
484
|
+ difflet.is_new(line) or difflet.is_common(line)):
|
|
485
|
+ hunk_line = line
|
|
486
|
+ diff._hunks[-1].append(hunk_line[0], hunk_line[1:])
|
447
|
487
|
|
448
|
|
- elif difflet.is_eof(stream[0]):
|
|
488
|
+ elif difflet.is_eof(line):
|
449
|
489
|
# ignore
|
450
|
|
- stream.pop(0)
|
|
490
|
+ pass
|
451
|
491
|
|
452
|
492
|
else:
|
453
|
493
|
# All other non-recognized lines are considered as headers or
|
454
|
494
|
# hunk headers respectively
|
455
|
495
|
#
|
456
|
|
- headers.append(stream.pop(0))
|
|
496
|
+ headers.append(line)
|
457
|
497
|
|
458
|
498
|
if headers:
|
459
|
499
|
raise RuntimeError('dangling header(s):\n%s' % ''.join(headers))
|
460
|
500
|
|
461
|
|
- # Validate the last patch set
|
462
|
|
- if out_diffs:
|
463
|
|
- assert out_diffs[-1]._old_path is not None
|
464
|
|
- assert out_diffs[-1]._new_path is not None
|
465
|
|
- assert len(out_diffs[-1]._hunks) > 0
|
466
|
|
- assert len(out_diffs[-1]._hunks[-1]._hunk_meta) > 0
|
467
|
|
-
|
468
|
|
- return out_diffs
|
|
501
|
+ # Validate and yield the last patch set
|
|
502
|
+ assert diff._old_path is not None
|
|
503
|
+ assert diff._new_path is not None
|
|
504
|
+ assert len(diff._hunks) > 0
|
|
505
|
+ assert len(diff._hunks[-1]._hunk_meta) > 0
|
|
506
|
+ yield diff
|
469
|
507
|
|
470
|
508
|
|
471
|
509
|
class DiffMarkup(object):
|
472
|
510
|
|
473
|
511
|
def __init__(self, stream):
|
474
|
|
- self._diffs = DiffParser(stream).get_diffs()
|
|
512
|
+ self._diffs = DiffParser(stream).get_diff_generator()
|
475
|
513
|
|
476
|
514
|
def markup(self, side_by_side=False, width=0):
|
477
|
515
|
"""Returns a generator"""
|
|
@@ -572,14 +610,10 @@ def main():
|
572
|
610
|
else:
|
573
|
611
|
diff_hdl = sys.stdin
|
574
|
612
|
|
575
|
|
- # FIXME: can't use generator for now due to current implementation in parser
|
576
|
|
- stream = [decode(line) for line in diff_hdl.readlines()]
|
577
|
|
-
|
578
|
|
- if diff_hdl is not sys.stdin:
|
579
|
|
- diff_hdl.close()
|
|
613
|
+ stream = PatchStream(diff_hdl)
|
580
|
614
|
|
581
|
615
|
# Don't let empty diff pass thru
|
582
|
|
- if not stream:
|
|
616
|
+ if stream.is_empty():
|
583
|
617
|
return 0
|
584
|
618
|
|
585
|
619
|
if opts.color == 'always' or (opts.color == 'auto' and sys.stdout.isatty()):
|
|
@@ -593,6 +627,9 @@ def main():
|
593
|
627
|
# pipe out stream untouched to make sure it is still a patch
|
594
|
628
|
sys.stdout.write(''.join(stream))
|
595
|
629
|
|
|
630
|
+ if diff_hdl is not sys.stdin:
|
|
631
|
+ diff_hdl.close()
|
|
632
|
+
|
596
|
633
|
return 0
|
597
|
634
|
|
598
|
635
|
|