Skip to content

Line normalization before diff #219

Closed

Description

@epictecch

Description

The DiffRowGenerator class offers the lineNormalizer property. By default, it is used to replace < and > by their escaped versions &lt; and &gt;.

The lineNormalizer is applied to the input texts before the diff is calculated. While I see this is as a useful feature, in case of the default settings it might be surprising that the resulting text might not have HTML escaping anymore:

final var generator = DiffRowGenerator.create() //
        .mergeOriginalRevised(true) //
        .showInlineDiffs(true) //
        .inlineDiffByWord(true) //
        .build();

final var rows = generator.generateDiffRows(List.of("hello <world>"), List.of("bye >world<"));

final var resultingText = rows.stream() //
        .map(DiffRow::getOldLine) //
        .collect(Collectors.joining(StringUtils.LF));

The resulting text is

<span class="editOldInline">hello</span><span class="editNewInline">bye</span> &<span class="editOldInline">lt</span><span class="editNewInline">gt</span>;world&<span class="editOldInline">gt</span><span class="editNewInline">lt</span>;

Note the part & is considered as an equal text part because both replacements &lt; and &gt; start with an ampersand. This resulting text is therefore no valid HTML anymore.

In order for this behaviour to be a problem, the following conditions must all be true:

  1. The inlineDiffByWord must be used
  2. The default lineNormalizer must be used
  3. The two provided texts must differ at a position which starts with a character that is replaced by the lineNormalizer
  4. A release >= 4.15 must be used.

Workaround
Override the lineNormalizer. E.g., by using the SPLIT_BY_WORD_PATTERN of release 4.12, in which the ampersand was not considered a character that splits words.

Solution approaches
IMHO, the SPLIT_BY_WORD_PATTERN of release 4.15+ is fine and I do not consider it to be the problem.

The library could offer one of the following features:

  1. a parameter which defines when the 'lineNormalizer' should be applied (before diff-ing or after)
  2. a second type of line-normalizer that is applied after diff-ing
  3. an option to have the library apply the processDiffs function to non-diffs as well

Activity

  1. epictecch commented on Oct 14, 2025

    @epictecch
    ContributorAuthor

    @wumpz Do you have any feedback/opinion on this topic? When you tell me which solution is the best fit for this library, I can submit a pull request.
    IMHO, my option 3 is the one that covers most use-cases and does not introduce new problems. Instead of adding an additional parameter to processDiffs() (whether the passed tag is a diff or an equality), we could add a new (-> backward compatible) feature: we could let the processDiffs() method remain as it is and we introduce a new processEqualities() method that defaults to not changing the tag.

  2. pinned this issue on Nov 5, 2025
  3. wumpz commented on Nov 5, 2025

    @wumpz
    Collaborator

    Seems as if processDiffs should be called anyway for Equal parts.

    Option 1 seems to be kind of there, since normalize is called before and after. Only the option is missing.

    Option 2: Do anyone need different normalizers (before, after). I don't think so.

    So frankly I am with you for option 3.

  4. added a commit that references this issue on Nov 29, 2025
    408c47e
  5. tusharsoni52 commented on Nov 29, 2025

    @tusharsoni52
    Contributor

    Hi @epictecch ,

    Thank you for detailing the problem and the solution options.
    Since @wumpz confirmed option 3, I implemented a PR following that design (#224).

    I’d really appreciate your review or feedback
    Happy to make any improvements.

    Thanks again for raising this issue and guiding the solution direction!

  6. added a commit that references this issue on Dec 10, 2025
    696edc4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions