Skip to content

Commit 4256dc7

Browse files
committed
Allow Removing Standard GFM Alert Types
1 parent cd6633f commit 4256dc7

5 files changed

Lines changed: 96 additions & 32 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ with the exception that 0.x versions can break between minor versions.
2020
other blocks (including other alerts). See
2121
[this section of the alerts README](./commonmark-ext-gfm-alerts/README.md#nesting-alerts)
2222
for more information.
23+
- New configuration for `AlertsExtension` to allow alert types (including standard
24+
GFM types) to be removed (disallowed).
25+
```java
26+
var extension = AlertsExtension.builder().removeTypes("NOTE", "TIP").build();
27+
```
2328

2429
## [0.28.0] - 2026-03-31
2530
### Added

commonmark-ext-gfm-alerts/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ var extension = AlertsExtension.builder()
3636

3737
Custom types must be UPPERCASE. Standard type titles can also be overridden for localization.
3838

39+
If any types (including the five standard GFM types) aren't desired, they can be
40+
removed (disallowed):
41+
42+
```java
43+
var extension = AlertsExtension.builder().removeTypes("NOTE", "TIP").build();
44+
```
45+
3946
### Custom Alert Titles
4047

4148
Allow authors to provide custom titles per alert by adding text after the alert

commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/AlertsExtension.java

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,28 @@
4949
public class AlertsExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension,
5050
MarkdownRenderer.MarkdownRendererExtension {
5151

52-
static final Set<String> STANDARD_TYPES = Set.of("NOTE", "TIP", "IMPORTANT", "WARNING", "CAUTION");
52+
/**
53+
* The standard GitHub Flavored Markdown (GFM) types that the extension
54+
* enables by default. They can be removed individually with
55+
* {@link Builder#removeTypes(String...)}.
56+
*/
57+
public static final Map<String, String> STANDARD_TYPES = Map.ofEntries(
58+
Map.entry("NOTE", "Note"),
59+
Map.entry("TIP", "Tip"),
60+
Map.entry("IMPORTANT", "Important"),
61+
Map.entry("WARNING", "Warning"),
62+
Map.entry("CAUTION", "Caution")
63+
);
5364

54-
private final Map<String, String> customTypes;
65+
/**
66+
* A map of alert marker ({@code [!TYPE]}) to the default title for that marker.
67+
*/
68+
private final Map<String, String> allowedTypes;
5569
private final boolean customTitlesAllowed;
5670
private final boolean nestedAlertsAllowed;
5771

5872
private AlertsExtension(Builder builder) {
59-
this.customTypes = new HashMap<>(builder.customTypes);
73+
this.allowedTypes = new HashMap<>(builder.allowedTypes);
6074
this.customTitlesAllowed = builder.customTitlesAllowed;
6175
this.nestedAlertsAllowed = builder.nestedAlertsAllowed;
6276
}
@@ -71,15 +85,14 @@ public static Builder builder() {
7185

7286
@Override
7387
public void extend(Parser.Builder parserBuilder) {
74-
var allowedTypes = new HashSet<>(STANDARD_TYPES);
75-
allowedTypes.addAll(customTypes.keySet());
88+
var allowedTypesSet = new HashSet<>(allowedTypes.keySet());
7689
parserBuilder.customBlockParserFactory(
77-
new AlertBlockParser.Factory(allowedTypes, customTitlesAllowed, nestedAlertsAllowed));
90+
new AlertBlockParser.Factory(allowedTypesSet, customTitlesAllowed, nestedAlertsAllowed));
7891
}
7992

8093
@Override
8194
public void extend(HtmlRenderer.Builder rendererBuilder) {
82-
rendererBuilder.nodeRendererFactory(context -> new AlertHtmlNodeRenderer(context, customTypes));
95+
rendererBuilder.nodeRendererFactory(context -> new AlertHtmlNodeRenderer(context, allowedTypes));
8396
}
8497

8598
@Override
@@ -101,18 +114,18 @@ public Set<Character> getSpecialCharacters() {
101114
* Builder for configuring the alerts extension.
102115
*/
103116
public static class Builder {
104-
private final Map<String, String> customTypes = new HashMap<>();
117+
private final Map<String, String> allowedTypes = new HashMap<>(STANDARD_TYPES);
105118
private boolean customTitlesAllowed = false;
106119
private boolean nestedAlertsAllowed = false;
107120

108121
/**
109-
* Adds a custom alert type with a display title.
122+
* Adds a custom alert type with a default title.
110123
* <p>
111-
* This can also be used to override the display title of standard GFM types
124+
* This can also be used to override the default title of standard GFM types
112125
* (e.g., for localization).
113126
*
114127
* @param type the alert type (must be uppercase)
115-
* @param title the display title for this alert type
128+
* @param title the default title for this alert type
116129
* @return {@code this}
117130
*/
118131
public Builder addCustomType(String type, String title) {
@@ -125,7 +138,34 @@ public Builder addCustomType(String type, String title) {
125138
if (!type.equals(type.toUpperCase(Locale.ROOT))) {
126139
throw new IllegalArgumentException("Type must be uppercase: " + type);
127140
}
128-
customTypes.put(type, title);
141+
allowedTypes.put(type, title);
142+
return this;
143+
}
144+
145+
/**
146+
* Removes alert types from the allowed list.
147+
*
148+
* @param types the alert types to remove (must be uppercase)
149+
* @return {@code this}
150+
* @see AlertsExtension#STANDARD_TYPES
151+
*/
152+
public Builder removeTypes(String... types) {
153+
if (types == null) {
154+
throw new IllegalArgumentException("Types must not be null");
155+
}
156+
157+
for (String type : types) {
158+
if (type == null || type.isEmpty()) {
159+
throw new IllegalArgumentException("Each type must not be null or empty");
160+
}
161+
162+
if (!type.equals(type.toUpperCase(Locale.ROOT))) {
163+
throw new IllegalArgumentException("Type must be uppercase: " + type);
164+
}
165+
166+
allowedTypes.remove(type);
167+
}
168+
129169
return this;
130170
}
131171

commonmark-ext-gfm-alerts/src/main/java/org/commonmark/ext/gfm/alerts/internal/AlertHtmlNodeRenderer.java

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ public class AlertHtmlNodeRenderer extends AlertNodeRenderer {
1313

1414
private final HtmlWriter htmlWriter;
1515
private final HtmlNodeRendererContext context;
16-
private final Map<String, String> customTypeTitles;
16+
private final Map<String, String> allowedTypes;
1717

18-
public AlertHtmlNodeRenderer(HtmlNodeRendererContext context, Map<String, String> customTypeTitles) {
18+
public AlertHtmlNodeRenderer(HtmlNodeRendererContext context, Map<String, String> allowedTypes) {
1919
this.htmlWriter = context.getWriter();
2020
this.context = context;
21-
this.customTypeTitles = customTypeTitles;
21+
this.allowedTypes = allowedTypes;
2222
}
2323

2424
@Override
@@ -53,24 +53,11 @@ protected void renderAlert(Alert alert) {
5353
}
5454

5555
private String getAlertTitle(String type) {
56-
var customTypeTitle = customTypeTitles.get(type);
57-
if (customTypeTitle != null) {
58-
return customTypeTitle;
59-
}
60-
switch (type) {
61-
case "NOTE":
62-
return "Note";
63-
case "TIP":
64-
return "Tip";
65-
case "IMPORTANT":
66-
return "Important";
67-
case "WARNING":
68-
return "Warning";
69-
case "CAUTION":
70-
return "Caution";
71-
default:
72-
throw new IllegalStateException("Unknown alert type: " + type);
56+
var typeTitle = allowedTypes.get(type);
57+
if (typeTitle == null) {
58+
throw new IllegalStateException("Unknown alert type: " + type);
7359
}
60+
return typeTitle;
7461
}
7562

7663
private void renderChildren(Node parent) {

commonmark-ext-gfm-alerts/src/test/java/org/commonmark/ext/gfm/alerts/AlertsTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,31 @@ public void customTypeTitleMustNotBeEmpty() {
137137
AlertsExtension.builder().addCustomType("INFO", "").build());
138138
}
139139

140+
@Test
141+
public void removeStandardTypes() {
142+
var extension = AlertsExtension.builder().removeTypes("NOTE", "TIP").build();
143+
var parser = Parser.builder().extensions(Set.of(extension)).build();
144+
var renderer = HtmlRenderer.builder().extensions(Set.of(extension)).build();
145+
146+
assertThat(renderer.render(parser.parse("> [!NOTE]\n> Regular block quote"))).isEqualTo(
147+
"<blockquote>\n" +
148+
"<p>[!NOTE]\n" +
149+
"Regular block quote</p>\n" +
150+
"</blockquote>\n");
151+
152+
assertThat(renderer.render(parser.parse("> [!TIP]\n> Regular block quote"))).isEqualTo(
153+
"<blockquote>\n" +
154+
"<p>[!TIP]\n" +
155+
"Regular block quote</p>\n" +
156+
"</blockquote>\n");
157+
158+
assertThat(renderer.render(parser.parse("> [!IMPORTANT]\n> Alert"))).isEqualTo(
159+
"<div class=\"markdown-alert markdown-alert-important\" data-alert-type=\"important\">\n" +
160+
"<p class=\"markdown-alert-title\">Important</p>\n" +
161+
"<p>Alert</p>\n" +
162+
"</div>\n");
163+
}
164+
140165
// Custom titles
141166

142167
@Test

0 commit comments

Comments
 (0)