def test_total_overlap_end(self): self.assertEqual( list( substitution.labeled_spans( substitution.Substitution(matched_spans={ 'a': (0, 10), 'b': (5, 10) }, primary_label='a'))), [ substitution.LabeledSpan(labels={'a'}, span=(0, 5)), substitution.LabeledSpan(labels={'a', 'b'}, span=(5, 10)) ])
def test_empty_range_next(self): self.assertEqual( list( substitution.labeled_spans( substitution.Substitution(matched_spans={ 'a': (0, 0), 'b': (1, 1) }, primary_label='a'))), [ substitution.LabeledSpan(labels={'a'}, span=(0, 0)), substitution.LabeledSpan(labels=set(), span=(0, 1)), substitution.LabeledSpan(labels={'b'}, span=(1, 1)) ])
def test_gap(self): self.assertEqual( list( substitution.labeled_spans( substitution.Substitution(matched_spans={ 'a': (0, 10), 'b': (20, 30) }, primary_label='a'))), [ substitution.LabeledSpan(labels={'a'}, span=(0, 10)), substitution.LabeledSpan(labels=set(), span=(10, 20)), substitution.LabeledSpan(labels={'b'}, span=(20, 30)) ])
def render(self, contents: str, sub: substitution.Substitution, extra: Mapping[str, Any]) -> Tuple[bool, str]: """Renders a single Substitution for display on the terminal. Args: contents: the original string the substitution is against. sub: a Substitution object. extra: extra substitution variables for the match format template. Returns: ``(is_diff, display)`` ``is_diff`` is ``True`` if this represents a diff, ``False`` if it represents a mere match with no replacement. ``display`` is a human-readable string to represent the substitution, printable to the terminal. """ restricted_diffs = substitution.as_diff(sub) # TODO: this is ridiculous, compute this somewhere else. # (e.g. include line_start/line_end as context in attributes on the # Substitution) match_start = min(start for (start, end) in sub.matched_spans.values()) match_end = max(end for (start, end) in sub.matched_spans.values()) line_start, line_end = line_expanded_span(contents, match_start, match_end) if sub.replacements: # include the surrounding context up to the line ends in the diff. extended_diffs = itertools.chain( [ substitution.LabeledSpan(labels=frozenset(), span=(line_start, match_start)) ], restricted_diffs, [ substitution.LabeledSpan(labels=frozenset(), span=(match_end, line_end)) ], ) rendered = [] def create_diff(prefix, s): if not s: # non-removals and non-additions should be elided from diff. return '' if not s.endswith('\n'): s += '\n' return ''.join(prefix + line for line in s.splitlines(True)) for is_diff, block in _sub_diff_blocks(contents, extended_diffs): if is_diff: rendered.append( create_diff( self._style(colorama.Fore.RED, '-'), self._render_diff_block(contents, sub, block, before=True, is_diff=True))) rendered.append( create_diff( self._style(colorama.Fore.GREEN, '+'), self._render_diff_block(contents, sub, block, before=False, is_diff=True))) else: rendered.append( create_diff( ' ', self._render_diff_block(contents, sub, block, before=True, is_diff=True))) return True, ''.join(rendered) else: display = self._render_diff_block(contents, sub, restricted_diffs, before=True, is_diff=False) head = contents[line_start:match_start] tail = contents[match_end:line_end] fmt_variables = extra.copy() fmt_variables.update(head=head, tail=tail, match=display) return False, self._match_format.format(**fmt_variables) + '\n'