diff --git a/docs/src/main/asciidoc/qute-reference.adoc b/docs/src/main/asciidoc/qute-reference.adoc
index 445731178971e..ae59583784fb4 100644
--- a/docs/src/main/asciidoc/qute-reference.adoc
+++ b/docs/src/main/asciidoc/qute-reference.adoc
@@ -80,7 +80,7 @@ NOTE: In Quarkus, the caching is done automatically.
The dynamic parts of a template include:
* *Comment*
-** `{! This is a comment !}`,
+** Starts with `{!` and ends with `!}`: `{! This is a comment !}`,
** Could be multi-line,
** May contain expressions and sections: `{! {#if true} !}`.
* *Expression*
@@ -93,6 +93,10 @@ The dynamic parts of a template include:
** The name in the closing tag is optional: `{#if active}ACTIVE!{/}`,
** Can be empty: `{#myTag image=true /}`,
** May declare nested section blocks: `{#if item.valid} Valid. {#else} Invalid. {/if}` and decide which block to render.
+* *Unparsed Character Data*
+** Starts with `{[` and ends with `]}`: `{[ ]}`,
+** Could be multi-line,
+** Used to mark the content that should be rendered but not parsed.
=== Identifiers
diff --git a/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java b/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java
index 7b7324a3d189f..b96899989989d 100644
--- a/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java
+++ b/independent-projects/qute/core/src/main/java/io/quarkus/qute/Parser.java
@@ -37,6 +37,8 @@ class Parser implements Function {
private static final char START_DELIMITER = '{';
private static final char END_DELIMITER = '}';
private static final char COMMENT_DELIMITER = '!';
+ private static final char CDATA_START_DELIMITER = '[';
+ private static final char CDATA_END_DELIMITER = ']';
private static final char UNDERSCORE = '_';
private static final char ESCAPE_CHAR = '\\';
@@ -151,6 +153,9 @@ private void processCharacter(char character) {
case COMMENT:
comment(character);
break;
+ case CDATA:
+ cdata(character);
+ break;
case TAG_CANDIDATE:
tagCandidate(character);
break;
@@ -193,6 +198,17 @@ private void comment(char character) {
}
}
+ private void cdata(char character) {
+ if (character == END_DELIMITER && buffer.length() > 0 && buffer.charAt(buffer.length() - 1) == CDATA_END_DELIMITER) {
+ // End of cdata
+ state = State.TEXT;
+ buffer.deleteCharAt(buffer.length() - 1);
+ flushText();
+ } else {
+ buffer.append(character);
+ }
+ }
+
private void tag(char character) {
if (character == END_DELIMITER) {
flushTag();
@@ -205,8 +221,15 @@ private void tagCandidate(char character) {
if (isValidIdentifierStart(character)) {
// Real tag start, flush text if any
flushText();
- state = character == COMMENT_DELIMITER ? State.COMMENT : State.TAG_INSIDE;
- buffer.append(character);
+ if (character == COMMENT_DELIMITER) {
+ buffer.append(character);
+ state = State.COMMENT;
+ } else if (character == CDATA_START_DELIMITER) {
+ state = State.CDATA;
+ } else {
+ buffer.append(character);
+ state = State.TAG_INSIDE;
+ }
} else {
// Ignore expressions/tags starting with an invalid identifier
buffer.append(START_DELIMITER).append(character);
@@ -219,8 +242,9 @@ private void tagCandidate(char character) {
}
private boolean isValidIdentifierStart(char character) {
- // A valid identifier must start with a digit, alphabet, underscore, comment delimiter or a tag command (e.g. # for sections)
- return Tag.isCommand(character) || character == COMMENT_DELIMITER || character == UNDERSCORE
+ // A valid identifier must start with a digit, alphabet, underscore, comment delimiter, cdata start delimiter or a tag command (e.g. # for sections)
+ return Tag.isCommand(character) || character == COMMENT_DELIMITER || character == CDATA_START_DELIMITER
+ || character == UNDERSCORE
|| Character.isDigit(character)
|| Character.isAlphabetic(character);
}
@@ -570,6 +594,7 @@ enum State {
TAG_CANDIDATE,
COMMENT,
ESCAPE,
+ CDATA,
}
diff --git a/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java b/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java
index a20a52f1fbcf7..d22372ac0caf7 100644
--- a/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java
+++ b/independent-projects/qute/core/src/test/java/io/quarkus/qute/ParserTest.java
@@ -165,6 +165,21 @@ public void testWhitespace() {
assertEquals("Hello world", engine.parse("Hello {name ?: 'world' }").render());
}
+ @Test
+ public void testCdata() {
+ Engine engine = Engine.builder().addDefaults().build();
+ String jsSnippet = "";
+ try {
+ engine.parse("Hello {name} " + jsSnippet);
+ fail();
+ } catch (Exception expected) {
+ }
+ assertEquals("Hello world ", engine.parse("Hello {name} {["
+ + jsSnippet
+ + "]}").data("name", "world").render());
+ assertEquals("Hello world ", engine.parse("Hello {name} {[]}").data("name", "world").render());
+ }
+
private void assertParserError(String template, String message, int line) {
Engine engine = Engine.builder().addDefaultSectionHelpers().build();
try {