diff --git a/VERSION-TODO.md b/VERSION-TODO.md index c33b2f74b..2f80615e6 100644 --- a/VERSION-TODO.md +++ b/VERSION-TODO.md @@ -7,6 +7,7 @@ - [Release 0.60.0](#release-0600) - [API Refactoring](#api-refactoring) - [Next 0.61.xx](#next-061xx) +- [0.61.14](#06114) - [0.61.12](#06112) - [0.61.10](#06110) - [0.61.8](#0618) @@ -218,6 +219,11 @@ Please give feedback on the upcoming changes if you have concerns about breaking * [ ] Fix: Html converter to not add spaces between end of inline marker and next punctuation: `.,:;` +## 0.61.14 + +* Fix: [#398, Autolinks get cut off if they contain \`&\` (escaped query params)] for + multiple html entities per link. + ## 0.61.12 * Fix: update docx4j to 8.1.6 diff --git a/VERSION.md b/VERSION.md index 10b4f4803..0d223083d 100644 --- a/VERSION.md +++ b/VERSION.md @@ -6,6 +6,7 @@ - [Release 0.60.0](#release-0600) - [API Refactoring](#api-refactoring) +- [0.61.14](#06114) - [0.61.12](#06112) - [0.61.10](#06110) - [0.61.8](#0618) @@ -162,6 +163,11 @@ Please give feedback on the upcoming changes if you have concerns about breaking * `com.vladsch.flexmark.util.ast.NodeAdaptingVisitHandler` * `com.vladsch.flexmark.util.ast.NodeAdaptingVisitor` +## 0.61.14 + +* Fix: [#398, Autolinks get cut off if they contain \`&\` (escaped query params)] for + multiple html entities per link. + ## 0.61.12 * Fix: update docx4j to 8.1.6 diff --git a/flexmark-ext-autolink/src/main/java/com/vladsch/flexmark/ext/autolink/internal/AutolinkNodePostProcessor.java b/flexmark-ext-autolink/src/main/java/com/vladsch/flexmark/ext/autolink/internal/AutolinkNodePostProcessor.java index e30fc6143..bd44cd4fd 100644 --- a/flexmark-ext-autolink/src/main/java/com/vladsch/flexmark/ext/autolink/internal/AutolinkNodePostProcessor.java +++ b/flexmark-ext-autolink/src/main/java/com/vladsch/flexmark/ext/autolink/internal/AutolinkNodePostProcessor.java @@ -108,13 +108,7 @@ public void process(@NotNull NodeTracker state, @NotNull Node node) { if (!htmlEntities.isEmpty()) { // need to replace all HTML entities in html entity regions - int lastOffset = 0; - - for (Range range : htmlEntities) { - if (lastOffset < range.getStart()) { - unescapedHtml = Escaping.unescapeHtml(original, range.getStart(), range.getEnd(), textMapper); - } - } + unescapedHtml = Escaping.unescapeHtml(original, htmlEntities, textMapper); } BasedSequence literal = Escaping.unescape(unescapedHtml, textMapper); diff --git a/flexmark-ext-autolink/src/test/resources/ext_autolink_ast_spec.md b/flexmark-ext-autolink/src/test/resources/ext_autolink_ast_spec.md index d15650593..c9c15418d 100644 --- a/flexmark-ext-autolink/src/test/resources/ext_autolink_ast_spec.md +++ b/flexmark-ext-autolink/src/test/resources/ext_autolink_ast_spec.md @@ -464,6 +464,19 @@ Document[0, 46] ```````````````````````````````` +```````````````````````````````` example Issues - 398: 2 +https://example.com/?first=hi&second=hello&third=ok +. +

https://example.com/?first=hi&amp;second=hello&amp;third=ok

+. +Document[0, 59] + Paragraph[0, 59] + TextBase[0, 59] chars:[0, 59, "https … rd=ok"] + AutoLink[0, 59] text:[0, 59, "https://example.com/?first=hi&second=hello&third=ok"] pageRef:[0, 59, "https://example.com/?first=hi&second=hello&third=ok"] + Text[0, 59] chars:[0, 59, "https … rd=ok"] +```````````````````````````````` + + ### xxx-1 Issue, Autolink extension does not convert URI prefix without following text diff --git a/flexmark-util-sequence/src/main/java/com/vladsch/flexmark/util/sequence/Escaping.java b/flexmark-util-sequence/src/main/java/com/vladsch/flexmark/util/sequence/Escaping.java index 20160888e..6230b2028 100644 --- a/flexmark-util-sequence/src/main/java/com/vladsch/flexmark/util/sequence/Escaping.java +++ b/flexmark-util-sequence/src/main/java/com/vladsch/flexmark/util/sequence/Escaping.java @@ -6,6 +6,7 @@ import org.jetbrains.annotations.Nullable; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -339,10 +340,10 @@ public static BasedSequence unescapeHtml(@NotNull BasedSequence s, @NotNull Repl * @return un-escaped sequence */ @NotNull - public static BasedSequence unescapeHtml(@NotNull BasedSequence s, int startOffset, int endOffset, @NotNull ReplacedTextMapper textMapper) { - int indexOfAny = s.indexOf('&', startOffset, endOffset); + public static BasedSequence unescapeHtml(@NotNull BasedSequence s, @NotNull List ranges, @NotNull ReplacedTextMapper textMapper) { + int indexOfAny = s.indexOf('&'); if (indexOfAny != -1) { - return replaceAll(ENTITY_ONLY, s, startOffset, endOffset, ENTITY_REPLACER, textMapper); + return replaceAll(ENTITY_ONLY, s, ranges, ENTITY_REPLACER, textMapper); } else { return s; } @@ -695,6 +696,35 @@ private static BasedSequence replaceAll(Pattern p, @NotNull BasedSequence s, int return textMapper.getReplacedSequence(); } + + @NotNull + private static BasedSequence replaceAll(Pattern p, @NotNull BasedSequence s, @NotNull List ranges, @NotNull Replacer replacer, @NotNull ReplacedTextMapper textMapper) { + Matcher matcher = p.matcher(s); + matcher.useTransparentBounds(false); + + if (textMapper.isModified()) { + textMapper.startNestedReplacement(s); + } + + int lastEnd = 0; + + for (Range range : ranges) { + matcher.region(range.getStart(), range.getEnd()); + + while (matcher.find()) { + textMapper.addOriginalText(lastEnd, matcher.start()); + replacer.replace(s, matcher.start(), matcher.end(), textMapper); + lastEnd = matcher.end(); + }; + } + + if (lastEnd < s.length()) { + textMapper.addOriginalText(lastEnd, s.length()); + } + + return textMapper.getReplacedSequence(); + } + interface Replacer { void replace(@NotNull String s, @NotNull StringBuilder sb);