mirror of
https://github.com/facebook/sapling.git
synced 2024-12-28 07:33:10 +03:00
mdiff: add a "blocksinrange" function to filter diff blocks by line range
The function filters diff blocks as generated by mdiff.allblock function based on whether they are contained in a given line range based on the "b-side" of blocks.
This commit is contained in:
parent
3ca85be38e
commit
dc8e8fcbf9
@ -113,6 +113,45 @@ def splitblock(base1, lines1, base2, lines2, opts):
|
||||
s1 = i1
|
||||
s2 = i2
|
||||
|
||||
def blocksinrange(blocks, rangeb):
|
||||
"""filter `blocks` like (a1, a2, b1, b2) from items outside line range
|
||||
`rangeb` from ``(b1, b2)`` point of view.
|
||||
|
||||
Return `filteredblocks, rangea` where:
|
||||
|
||||
* `filteredblocks` is list of ``block = (a1, a2, b1, b2), stype`` items of
|
||||
`blocks` that are inside `rangeb` from ``(b1, b2)`` point of view; a
|
||||
block ``(b1, b2)`` being inside `rangeb` if
|
||||
``rangeb[0] < b2 and b1 < rangeb[1]``;
|
||||
* `rangea` is the line range w.r.t. to ``(a1, a2)`` parts of `blocks`.
|
||||
"""
|
||||
lbb, ubb = rangeb
|
||||
lba, uba = None, None
|
||||
filteredblocks = []
|
||||
for block in blocks:
|
||||
(a1, a2, b1, b2), stype = block
|
||||
if lbb >= b1 and ubb <= b2 and stype == '=':
|
||||
# rangeb is within a single "=" hunk, restrict back linerange1
|
||||
# by offsetting rangeb
|
||||
lba = lbb - b1 + a1
|
||||
uba = ubb - b1 + a1
|
||||
else:
|
||||
if b1 <= lbb < b2:
|
||||
if stype == '=':
|
||||
lba = a2 - (b2 - lbb)
|
||||
else:
|
||||
lba = a1
|
||||
if b1 < ubb <= b2:
|
||||
if stype == '=':
|
||||
uba = a1 + (ubb - b1)
|
||||
else:
|
||||
uba = a2
|
||||
if lbb < b2 and b1 < ubb:
|
||||
filteredblocks.append(block)
|
||||
if lba is None or uba is None or uba < lba:
|
||||
raise error.Abort(_('line range exceeds file size'))
|
||||
return filteredblocks, (lba, uba)
|
||||
|
||||
def allblocks(text1, text2, opts=None, lines1=None, lines2=None):
|
||||
"""Return (block, type) tuples, where block is an mdiff.blocks
|
||||
line entry. type is '=' for blocks matching exactly one another
|
||||
|
232
tests/test-linerange.py
Normal file
232
tests/test-linerange.py
Normal file
@ -0,0 +1,232 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
from mercurial import error, mdiff
|
||||
|
||||
# for readability, line numbers are 0-origin
|
||||
text1 = '''
|
||||
00 at OLD
|
||||
01 at OLD
|
||||
02 at OLD
|
||||
02 at NEW, 03 at OLD
|
||||
03 at NEW, 04 at OLD
|
||||
04 at NEW, 05 at OLD
|
||||
05 at NEW, 06 at OLD
|
||||
07 at OLD
|
||||
08 at OLD
|
||||
09 at OLD
|
||||
10 at OLD
|
||||
11 at OLD
|
||||
'''[1:] # strip initial LF
|
||||
|
||||
text2 = '''
|
||||
00 at NEW
|
||||
01 at NEW
|
||||
02 at NEW, 03 at OLD
|
||||
03 at NEW, 04 at OLD
|
||||
04 at NEW, 05 at OLD
|
||||
05 at NEW, 06 at OLD
|
||||
06 at NEW
|
||||
07 at NEW
|
||||
08 at NEW
|
||||
09 at NEW
|
||||
10 at NEW
|
||||
11 at NEW
|
||||
'''[1:] # strip initial LF
|
||||
|
||||
def filteredblocks(blocks, rangeb):
|
||||
"""return `rangea` extracted from `blocks` coming from
|
||||
`mdiff.blocksinrange` along with the mask of blocks within rangeb.
|
||||
"""
|
||||
filtered, rangea = mdiff.blocksinrange(blocks, rangeb)
|
||||
skipped = [b not in filtered for b in blocks]
|
||||
return rangea, skipped
|
||||
|
||||
class blocksinrangetests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.blocks = list(mdiff.allblocks(text1, text2))
|
||||
assert self.blocks == [
|
||||
([0, 3, 0, 2], '!'),
|
||||
((3, 7, 2, 6), '='),
|
||||
([7, 12, 6, 12], '!'),
|
||||
((12, 12, 12, 12), '='),
|
||||
], self.blocks
|
||||
|
||||
def testWithinEqual(self):
|
||||
"""linerange within an "=" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^^
|
||||
linerange2 = (3, 5)
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (4, 6))
|
||||
self.assertEqual(skipped, [True, False, True, True])
|
||||
|
||||
def testWithinEqualStrictly(self):
|
||||
"""linerange matching exactly an "=" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^^^^
|
||||
linerange2 = (2, 6)
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (3, 7))
|
||||
self.assertEqual(skipped, [True, False, True, True])
|
||||
|
||||
def testWithinEqualLowerbound(self):
|
||||
"""linerange at beginning of an "=" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^^
|
||||
linerange2 = (2, 4)
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (3, 5))
|
||||
self.assertEqual(skipped, [True, False, True, True])
|
||||
|
||||
def testWithinEqualLowerboundOneline(self):
|
||||
"""oneline-linerange at beginning of an "=" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^
|
||||
linerange2 = (2, 3)
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (3, 4))
|
||||
self.assertEqual(skipped, [True, False, True, True])
|
||||
|
||||
def testWithinEqualUpperbound(self):
|
||||
"""linerange at end of an "=" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^^^
|
||||
linerange2 = (3, 6)
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (4, 7))
|
||||
self.assertEqual(skipped, [True, False, True, True])
|
||||
|
||||
def testWithinEqualUpperboundOneLine(self):
|
||||
"""oneline-linerange at end of an "=" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^
|
||||
linerange2 = (5, 6)
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (6, 7))
|
||||
self.assertEqual(skipped, [True, False, True, True])
|
||||
|
||||
def testWithinFirstBlockNeq(self):
|
||||
"""linerange within the first "!" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^
|
||||
# | (empty)
|
||||
# ^
|
||||
# ^^
|
||||
for linerange2 in [
|
||||
(0, 1),
|
||||
(1, 1),
|
||||
(1, 2),
|
||||
(0, 2),
|
||||
]:
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (0, 3))
|
||||
self.assertEqual(skipped, [False, True, True, True])
|
||||
|
||||
def testWithinLastBlockNeq(self):
|
||||
"""linerange within the last "!" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^
|
||||
# ^
|
||||
# | (empty)
|
||||
# ^^^^^^
|
||||
# ^
|
||||
for linerange2 in [
|
||||
(6, 7),
|
||||
(7, 8),
|
||||
(7, 7),
|
||||
(6, 12),
|
||||
(11, 12),
|
||||
]:
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (7, 12))
|
||||
self.assertEqual(skipped, [True, True, False, True])
|
||||
|
||||
def testAccrossTwoBlocks(self):
|
||||
"""linerange accross two blocks"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^^^^
|
||||
linerange2 = (1, 5)
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (0, 6))
|
||||
self.assertEqual(skipped, [False, False, True, True])
|
||||
|
||||
def testCrossingSeveralBlocks(self):
|
||||
"""linerange accross three blocks"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^^^^^^^
|
||||
linerange2 = (1, 8)
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, (0, 12))
|
||||
self.assertEqual(skipped, [False, False, False, True])
|
||||
|
||||
def testStartInEqBlock(self):
|
||||
"""linerange starting in an "=" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^^^^
|
||||
# ^^^^^^^
|
||||
for linerange2, expectedlinerange1 in [
|
||||
((5, 9), (6, 12)),
|
||||
((4, 11), (5, 12)),
|
||||
]:
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, expectedlinerange1)
|
||||
self.assertEqual(skipped, [True, False, False, True])
|
||||
|
||||
def testEndInEqBlock(self):
|
||||
"""linerange ending in an "=" block"""
|
||||
# IDX 0 1
|
||||
# 012345678901
|
||||
# SRC NNOOOONNNNNN (New/Old)
|
||||
# ^^
|
||||
# ^^^^^
|
||||
for linerange2, expectedlinerange1 in [
|
||||
((1, 3), (0, 4)),
|
||||
((0, 4), (0, 5)),
|
||||
]:
|
||||
linerange1, skipped = filteredblocks(self.blocks, linerange2)
|
||||
self.assertEqual(linerange1, expectedlinerange1)
|
||||
self.assertEqual(skipped, [False, False, True, True])
|
||||
|
||||
def testOutOfRange(self):
|
||||
"""linerange exceeding file size"""
|
||||
exctype = error.Abort
|
||||
for linerange2 in [
|
||||
(0, 34),
|
||||
(15, 12),
|
||||
]:
|
||||
# Could be `with self.assertRaises(error.Abort)` but python2.6
|
||||
# does not have assertRaises context manager.
|
||||
try:
|
||||
mdiff.blocksinrange(self.blocks, linerange2)
|
||||
except exctype as exc:
|
||||
self.assertTrue('line range exceeds file size' in str(exc))
|
||||
else:
|
||||
self.fail('%s not raised' % exctype.__name__)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import silenttestrunner
|
||||
silenttestrunner.main(__name__)
|
Loading…
Reference in New Issue
Block a user