Skip to content

Commit 7ba63e2

Browse files
committed
Allow Removing Standard GFM Alert Types
1 parent d09c3c0 commit 7ba63e2

5 files changed

Lines changed: 81 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().removeType("NOTE").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 type (including the five standard GFM types) aren't desired, they can be
40+
removed (disallowed):
41+
42+
```java
43+
var extension = AlertsExtension.builder().removeType("NOTE").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: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import org.commonmark.ext.gfm.alerts.internal.AlertMarkdownNodeRenderer;
77
import org.commonmark.parser.Parser;
88
import org.commonmark.renderer.NodeRenderer;
9+
import org.commonmark.renderer.html.HtmlNodeRendererContext;
10+
import org.commonmark.renderer.html.HtmlNodeRendererFactory;
911
import org.commonmark.renderer.html.HtmlRenderer;
1012
import org.commonmark.renderer.markdown.MarkdownNodeRendererContext;
1113
import org.commonmark.renderer.markdown.MarkdownNodeRendererFactory;
@@ -49,14 +51,28 @@
4951
public class AlertsExtension implements Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension,
5052
MarkdownRenderer.MarkdownRendererExtension {
5153

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

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

5874
private AlertsExtension(Builder builder) {
59-
this.customTypes = new HashMap<>(builder.customTypes);
75+
this.allowedTypes = new HashMap<>(builder.allowedTypes);
6076
this.customTitlesAllowed = builder.customTitlesAllowed;
6177
this.nestedAlertsAllowed = builder.nestedAlertsAllowed;
6278
}
@@ -71,15 +87,19 @@ public static Builder builder() {
7187

7288
@Override
7389
public void extend(Parser.Builder parserBuilder) {
74-
var allowedTypes = new HashSet<>(STANDARD_TYPES);
75-
allowedTypes.addAll(customTypes.keySet());
90+
var allowedTypesSet = new HashSet<>(allowedTypes.keySet());
7691
parserBuilder.customBlockParserFactory(
77-
new AlertBlockParser.Factory(allowedTypes, customTitlesAllowed, nestedAlertsAllowed));
92+
new AlertBlockParser.Factory(allowedTypesSet, customTitlesAllowed, nestedAlertsAllowed));
7893
}
7994

8095
@Override
8196
public void extend(HtmlRenderer.Builder rendererBuilder) {
82-
rendererBuilder.nodeRendererFactory(context -> new AlertHtmlNodeRenderer(context, customTypes));
97+
rendererBuilder.nodeRendererFactory(new HtmlNodeRendererFactory() {
98+
@Override
99+
public NodeRenderer create(HtmlNodeRendererContext context) {
100+
return new AlertHtmlNodeRenderer(context, allowedTypes);
101+
}
102+
});
83103
}
84104

85105
@Override
@@ -101,18 +121,18 @@ public Set<Character> getSpecialCharacters() {
101121
* Builder for configuring the alerts extension.
102122
*/
103123
public static class Builder {
104-
private final Map<String, String> customTypes = new HashMap<>();
124+
private final Map<String, String> allowedTypes = new HashMap<>(STANDARD_TYPES);
105125
private boolean customTitlesAllowed = false;
106126
private boolean nestedAlertsAllowed = false;
107127

108128
/**
109-
* Adds a custom alert type with a display title.
129+
* Adds a custom alert type with a default title.
110130
* <p>
111-
* This can also be used to override the display title of standard GFM types
131+
* This can also be used to override the default title of standard GFM types
112132
* (e.g., for localization).
113133
*
114134
* @param type the alert type (must be uppercase)
115-
* @param title the display title for this alert type
135+
* @param title the default title for this alert type
116136
* @return {@code this}
117137
*/
118138
public Builder addCustomType(String type, String title) {
@@ -125,7 +145,24 @@ public Builder addCustomType(String type, String title) {
125145
if (!type.equals(type.toUpperCase(Locale.ROOT))) {
126146
throw new IllegalArgumentException("Type must be uppercase: " + type);
127147
}
128-
customTypes.put(type, title);
148+
allowedTypes.put(type, title);
149+
return this;
150+
}
151+
152+
/**
153+
* Removes an alert type.
154+
*
155+
* @param type the alert type to remove (must be uppercase)
156+
* @return {@code this}
157+
*/
158+
public Builder removeType(String type) {
159+
if (type == null || type.isEmpty()) {
160+
throw new IllegalArgumentException("Type must not be null or empty");
161+
}
162+
if (!type.equals(type.toUpperCase(Locale.ROOT))) {
163+
throw new IllegalArgumentException("Type must be uppercase: " + type);
164+
}
165+
allowedTypes.remove(type);
129166
return this;
130167
}
131168

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: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,19 @@ public void customTypeTitleMustNotBeEmpty() {
137137
AlertsExtension.builder().addCustomType("INFO", "").build());
138138
}
139139

140+
@Test
141+
public void removeStandardType() {
142+
Extension extension = AlertsExtension.builder().removeType("NOTE").build();
143+
Parser parser = Parser.builder().extensions(Set.of(extension)).build();
144+
HtmlRenderer 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+
140153
// Custom titles
141154

142155
@Test

0 commit comments

Comments
 (0)