Skip to content

Commit b863f65

Browse files
committed
Escape Markdown styling in issue titles
Closes gh-50
1 parent ce4e1bd commit b863f65

File tree

2 files changed

+46
-1
lines changed

2 files changed

+46
-1
lines changed

src/main/java/io/spring/githubchangeloggenerator/ChangelogGenerator.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public class ChangelogGenerator {
5656
private static final Comparator<Issue> TITLE_COMPARATOR = Comparator.comparing(Issue::getTitle,
5757
String.CASE_INSENSITIVE_ORDER);
5858

59-
private static final List<Escape> escapes = Arrays.asList(gitHubUserMentions(), htmlTags());
59+
private static final List<Escape> escapes = Arrays.asList(gitHubUserMentions(), htmlTags(), markdownStyling());
6060

6161
private final GitHubService service;
6262

@@ -247,6 +247,21 @@ private static Escape htmlTags() {
247247
return new PatternEscape(Pattern.compile("(^|[^\\w`])(<[\\w\\-/<>]+>)"), "$1`$2`");
248248
}
249249

250+
private static Escape markdownStyling() {
251+
return (input) -> {
252+
char previous = ' ';
253+
StringBuilder result = new StringBuilder(input.length());
254+
for (char c : input.toCharArray()) {
255+
if (previous != '\\' && c == '*' || c == '_' || c == '~') {
256+
result.append('\\');
257+
}
258+
result.append(c);
259+
previous = c;
260+
}
261+
return result.toString();
262+
};
263+
}
264+
250265
private interface Escape {
251266

252267
String apply(String input);

src/test/java/io/spring/githubchangeloggenerator/ChangelogGeneratorTests.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,36 @@ void generateWhenEscapedHtmlTagIsInIssueTitleItIsNotEscapedAgain() throws IOExce
262262
assertThat(new String(Files.readAllBytes(file))).contains("Bug 1 for `<td>`");
263263
}
264264

265+
@Test
266+
void generateWhenMarkdownStylingIsInIssueTitleItIsEscaped() throws IOException {
267+
setupGenerator(MilestoneReference.TITLE);
268+
List<Issue> issues = new ArrayList<>();
269+
issues.add(newIssue("Bug 1 for *italic*", "1", "bug-1-url", Type.BUG));
270+
issues.add(newIssue("Bug 2 for _italic_", "2", "bug-2-url", Type.BUG));
271+
issues.add(newIssue("Bug 3 for **bold**", "3", "bug-3-url", Type.BUG));
272+
issues.add(newIssue("Bug 4 for __bold__", "4", "bug-4-url", Type.BUG));
273+
issues.add(newIssue("Bug 5 for ~strikethrough~", "4", "bug-4-url", Type.BUG));
274+
given(this.service.getMilestoneNumber("v2.3", REPO)).willReturn(23);
275+
given(this.service.getIssuesForMilestone(23, REPO)).willReturn(issues);
276+
Path file = generateChangelog("v2.3");
277+
assertThat(new String(Files.readAllBytes(file))).contains("Bug 1 for \\*italic\\*");
278+
assertThat(new String(Files.readAllBytes(file))).contains("Bug 2 for \\_italic\\_");
279+
assertThat(new String(Files.readAllBytes(file))).contains("Bug 3 for \\*\\*bold\\*\\*");
280+
assertThat(new String(Files.readAllBytes(file))).contains("Bug 4 for \\_\\_bold\\_\\_");
281+
assertThat(new String(Files.readAllBytes(file))).contains("Bug 5 for \\~strikethrough\\~");
282+
}
283+
284+
@Test
285+
void generateWhenEscapedMarkdownStylingIsInIssueTitleItIsNotEscapedAgain() throws IOException {
286+
setupGenerator(MilestoneReference.TITLE);
287+
List<Issue> issues = new ArrayList<>();
288+
issues.add(newIssue("Bug 1 for `<td>`", "1", "bug-1-url", Type.BUG));
289+
given(this.service.getMilestoneNumber("v2.3", REPO)).willReturn(23);
290+
given(this.service.getIssuesForMilestone(23, REPO)).willReturn(issues);
291+
Path file = generateChangelog("v2.3");
292+
assertThat(new String(Files.readAllBytes(file))).contains("Bug 1 for `<td>`");
293+
}
294+
265295
@Test
266296
void generateWhenSectionSortedByTitle() throws Exception {
267297
List<Section> sections = new ArrayList<>();

0 commit comments

Comments
 (0)