11package org .commonmark .ext .gfm .alerts ;
22
33import org .commonmark .Extension ;
4- import org .commonmark .ext .gfm .alerts .internal .AlertPostProcessor ;
4+ import org .commonmark .ext .gfm .alerts .internal .AlertBlockParser ;
55import org .commonmark .ext .gfm .alerts .internal .AlertHtmlNodeRenderer ;
66import org .commonmark .ext .gfm .alerts .internal .AlertMarkdownNodeRenderer ;
77import org .commonmark .parser .Parser ;
2626 * ({@link org.commonmark.parser.Parser.Builder#extensions(Iterable)},
2727 * {@link HtmlRenderer.Builder#extensions(Iterable)}).
2828 * Parsed alerts become {@link Alert} blocks.
29+ *
30+ * The {@link #create() default configuration} of this extension will match GFM
31+ * exactly, with the following exceptions:
32+ *
33+ * - Alert markers take precedence over link reference definitions.
34+ * - Lazy continuation is not allowed between the marker and the body text. Example:
35+ *
36+ * <pre>{@code
37+ * > [!NOTE]
38+ * Lazy body text will be parsed as a new paragraph
39+ * }</pre>
2940 */
3041public class AlertsExtension implements Parser .ParserExtension , HtmlRenderer .HtmlRendererExtension ,
3142 MarkdownRenderer .MarkdownRendererExtension {
3243
3344 static final Set <String > STANDARD_TYPES = Set .of ("NOTE" , "TIP" , "IMPORTANT" , "WARNING" , "CAUTION" );
3445
3546 private final Map <String , String > customTypes ;
47+ private final boolean customTitlesAllowed ;
48+ private final boolean nestedAlertsAllowed ;
3649
3750 private AlertsExtension (Builder builder ) {
3851 this .customTypes = new HashMap <>(builder .customTypes );
52+ this .customTitlesAllowed = builder .customTitlesAllowed ;
53+ this .nestedAlertsAllowed = builder .nestedAlertsAllowed ;
3954 }
4055
4156 public static Extension create () {
@@ -50,7 +65,8 @@ public static Builder builder() {
5065 public void extend (Parser .Builder parserBuilder ) {
5166 var allowedTypes = new HashSet <>(STANDARD_TYPES );
5267 allowedTypes .addAll (customTypes .keySet ());
53- parserBuilder .postProcessor (new AlertPostProcessor (allowedTypes ));
68+ parserBuilder .customBlockParserFactory (
69+ new AlertBlockParser .Factory (allowedTypes , customTitlesAllowed , nestedAlertsAllowed ));
5470 }
5571
5672 @ Override
@@ -83,6 +99,8 @@ public Set<Character> getSpecialCharacters() {
8399 */
84100 public static class Builder {
85101 private final Map <String , String > customTypes = new HashMap <>();
102+ private boolean customTitlesAllowed = false ;
103+ private boolean nestedAlertsAllowed = false ;
86104
87105 /**
88106 * Adds a custom alert type with a display title.
@@ -108,6 +126,47 @@ public Builder addCustomType(String type, String title) {
108126 return this ;
109127 }
110128
129+ /**
130+ * Allows custom titles on alerts. See {@link AlertTitle} for more information.
131+ * @return {@code this}
132+ */
133+ public Builder allowCustomTitles () {
134+ customTitlesAllowed = true ;
135+ return this ;
136+ }
137+
138+ /**
139+ * Disallows custom titles on alerts. See {@link AlertTitle} for more information.
140+ * @return {@code this}
141+ */
142+ public Builder disallowCustomTitles () {
143+ customTitlesAllowed = false ;
144+ return this ;
145+ }
146+
147+ /**
148+ * Allows alerts to be parsed within blocks other than {@code Document} (the root).
149+ * <p>
150+ * Note that even with this enabled, {@link Parser.Builder#maxOpenBlockParsers(int)}
151+ * will be respected.
152+ * @return {@code this}
153+ */
154+ public Builder allowNestedAlerts () {
155+ nestedAlertsAllowed = true ;
156+ return this ;
157+ }
158+
159+ /**
160+ * Prevents alerts from being parsed within blocks other than {@code Document}
161+ * (the root). If an alert appears within another block, it will be parsed as
162+ * a regular {@code BlockQuote}.
163+ * @return {@code this}
164+ */
165+ public Builder disallowNestedAlerts () {
166+ nestedAlertsAllowed = false ;
167+ return this ;
168+ }
169+
111170 /**
112171 * @return a configured {@link Extension}
113172 */
0 commit comments