'Isolate change clusters by eliminating ranges with no changes.
Return a generator of groups with upto n lines of context.
Each group is in the same format as returned by get_opcodes().
>>> from pprint import pprint
>>> a = map(str, range(1,40))
>>> b = a[:]
>>> b[8:8] = [\'i\']     # Make an insertion
>>> b[20] += \'x\'       # Make a replacement
>>> b[23:28] = []      # Make a deletion
>>> b[30] += \'y\'       # Make another replacement
>>> pprint(list(SequenceMatcher(None,a,b).get_grouped_opcodes()))
[[(\'equal\', 5, 8, 5, 8), (\'insert\', 8, 8, 8, 9), (\'equal\', 8, 11, 9, 12)],
[(\'equal\', 16, 19, 17, 20),
(\'replace\', 19, 20, 20, 21),
(\'equal\', 20, 22, 21, 23),
(\'delete\', 22, 27, 23, 23),
(\'equal\', 27, 30, 23, 26)],
[(\'equal\', 31, 34, 27, 30),
(\'replace\', 34, 35, 30, 31),
(\'equal\', 35, 38, 31, 34)]]'

def get_grouped_opcodes(self, n=3):

    codes = self.get_opcodes()
    if (not codes):
        codes = [('equal', 0, 1, 0, 1)]
    if (codes[0][0] == 'equal'):
        (tag, i1, i2, j1, j2) = codes[0]
        codes[0] = (tag, max(i1, (i2 - n)), i2, max(j1, (j2 - n)), j2)
    if (codes[(-1)][0] == 'equal'):
        (tag, i1, i2, j1, j2) = codes[(-1)]
        codes[(-1)] = (tag, i1, min(i2, (i1 + n)), j1, min(j2, (j1 + n)))
    nn = (n + n)
    group = []
    for (tag, i1, i2, j1, j2) in codes:
        if ((tag == 'equal') and ((i2 - i1) > nn)):
            group.append((tag, i1, min(i2, (i1 + n)), j1, min(j2, (j1 + n))))
            (yield group)
            group = []
            (i1, j1) = (max(i1, (i2 - n)), max(j1, (j2 - n)))
        group.append((tag, i1, i2, j1, j2))
    if (group and (not ((len(group) == 1) and (group[0][0] == 'equal')))):
        (yield group)
