diff --git a/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java b/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java
index 190f64dd2be6..8a4732ba9354 100644
--- a/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java
+++ b/java/java-psi-impl/src/com/intellij/lang/java/lexer/JavaLexer.java
@@ -55,7 +55,8 @@ public final class JavaLexer extends LexerBase {
private int myTokenEndOffset; // positioned after the last symbol of the current token
private IElementType myTokenType;
private short myState = 0;
- private short myBraceCount = 0;
+ private short[] myBraceCounts = new short[1];
+ private int myTemplateDepth = -1;
/** The length of the last valid unicode escape (6 or greater), or 1 when no unicode escape was found. */
private int mySymbolLength = 1;
@@ -76,14 +77,18 @@ public final class JavaLexer extends LexerBase {
mySymbolLength = 1;
if (initialState != 0) {
myState = (short)initialState;
- myBraceCount = (short)(initialState >> 16);
+ short braceCount = (short)(initialState >> 16);
+ if (braceCount > 0) {
+ myBraceCounts[0] = braceCount;
+ myTemplateDepth = 0;
+ }
}
myFlexLexer.reset(myBuffer, startOffset, endOffset, 0);
}
@Override
public int getState() {
- return (myBraceCount == 0) ? 0 : myBraceCount << 16 | myState;
+ return myTemplateDepth < 0 ? 0 : myBraceCounts[myTemplateDepth] << 16 | myState;
}
@Override
@@ -135,16 +140,16 @@ public final class JavaLexer extends LexerBase {
break;
case '{':
- if (myBraceCount > 0) {
- myBraceCount++;
+ if (myTemplateDepth >= 0) {
+ myBraceCounts[myTemplateDepth]++;
}
myTokenType = JavaTokenType.LBRACE;
myTokenEndOffset = myBufferIndex + mySymbolLength;
break;
case '}':
- if (myBraceCount > 0) {
- myBraceCount--;
- if (myBraceCount == 0) {
+ if (myTemplateDepth >= 0) {
+ if (--myBraceCounts[myTemplateDepth] == 0) {
+ myTemplateDepth--;
if (myState == STATE_TEXT_BLOCK_TEMPLATE) {
if (locateTextBlockEnd(myBufferIndex + mySymbolLength)) {
myTokenType = JavaTokenType.TEXT_BLOCK_TEMPLATE_MID;
@@ -287,7 +292,11 @@ public final class JavaLexer extends LexerBase {
if (pos < myBufferEndOffset) {
if (locateCharAt(pos) == '{' && myStringTemplates && quoteChar == '"') {
pos += mySymbolLength;
- myBraceCount = 1;
+ myTemplateDepth++;
+ if (myTemplateDepth == myBraceCounts.length) {
+ myBraceCounts = Arrays.copyOf(myBraceCounts, myTemplateDepth * 2);
+ }
+ myBraceCounts[myTemplateDepth] = 1;
myTokenEndOffset = pos;
return true;
}
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java
index 206eb03f53bd..23b1d3df70d3 100644
--- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java
+++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlightingStringTemplates/StringTemplates.java
@@ -36,4 +36,8 @@ class X {
System.out.println(myProcessor."");
return processor."\{}\{}\{}\{}\{}\{}";
}
+
+ void nested() {
+ System.out.println(STR."\{STR."\{STR."\{STR."\{STR."\{STR."\{STR.""}"}"}"}"}"}");
+ }
}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/java/lexer/JavaLexerTest.java b/java/java-tests/testSrc/com/intellij/java/lexer/JavaLexerTest.java
index 935e8a8ddc55..0c530816177f 100644
--- a/java/java-tests/testSrc/com/intellij/java/lexer/JavaLexerTest.java
+++ b/java/java-tests/testSrc/com/intellij/java/lexer/JavaLexerTest.java
@@ -333,7 +333,64 @@ public class JavaLexerTest extends LexerTestCase {
WHITE_SPACE (' ')
TEXT_BLOCK_TEMPLATE_END ('}""\"')
WHITE_SPACE (' ')""");
- //doTest("\"\\{fruit[0]}, \\{STR.\"\\{fruit[1]}, \\{fruit[2]}\"}\"","");
+ doTest("""
+ "\\{fruit[0]}, \\{STR."\\{fruit[1]}, \\{fruit[2]}"}"
+ """,
+ """
+ STRING_TEMPLATE_BEGIN ('"\\{')
+ IDENTIFIER ('fruit')
+ LBRACKET ('[')
+ INTEGER_LITERAL ('0')
+ RBRACKET (']')
+ STRING_TEMPLATE_MID ('}, \\{')
+ IDENTIFIER ('STR')
+ DOT ('.')
+ STRING_TEMPLATE_BEGIN ('"\\{')
+ IDENTIFIER ('fruit')
+ LBRACKET ('[')
+ INTEGER_LITERAL ('1')
+ RBRACKET (']')
+ STRING_TEMPLATE_MID ('}, \\{')
+ IDENTIFIER ('fruit')
+ LBRACKET ('[')
+ INTEGER_LITERAL ('2')
+ RBRACKET (']')
+ STRING_TEMPLATE_END ('}"')
+ STRING_TEMPLATE_END ('}"')
+ WHITE_SPACE ('\\n')""");
+ doTest("""
+ STR."\\{STR."\\{STR."\\{STR."\\{STR."\\{STR."\\{STR.""}"}"}"}"}"}"
+ """,
+ """
+ IDENTIFIER ('STR')
+ DOT ('.')
+ STRING_TEMPLATE_BEGIN ('"\\{')
+ IDENTIFIER ('STR')
+ DOT ('.')
+ STRING_TEMPLATE_BEGIN ('"\\{')
+ IDENTIFIER ('STR')
+ DOT ('.')
+ STRING_TEMPLATE_BEGIN ('"\\{')
+ IDENTIFIER ('STR')
+ DOT ('.')
+ STRING_TEMPLATE_BEGIN ('"\\{')
+ IDENTIFIER ('STR')
+ DOT ('.')
+ STRING_TEMPLATE_BEGIN ('"\\{')
+ IDENTIFIER ('STR')
+ DOT ('.')
+ STRING_TEMPLATE_BEGIN ('"\\{')
+ IDENTIFIER ('STR')
+ DOT ('.')
+ STRING_LITERAL ('""')
+ STRING_TEMPLATE_END ('}"')
+ STRING_TEMPLATE_END ('}"')
+ STRING_TEMPLATE_END ('}"')
+ STRING_TEMPLATE_END ('}"')
+ STRING_TEMPLATE_END ('}"')
+ STRING_TEMPLATE_END ('}"')
+ WHITE_SPACE ('\\n')
+ """);
}
public void testStringLiterals() {
@@ -404,7 +461,8 @@ public class JavaLexerTest extends LexerTestCase {
doTest("/", "DIV ('/')");
doTest("1/2", "INTEGER_LITERAL ('1')\nDIV ('/')\nINTEGER_LITERAL ('2')");
doTest("//\\\\u000A test", "END_OF_LINE_COMMENT ('//\\\\u000A test')"); // escaped backslash, not a unicode escape
- doTest("//\\\\\\u000A test", "END_OF_LINE_COMMENT ('//\\\\')\nWHITE_SPACE ('\\u000A ')\nIDENTIFIER ('test')"); // escaped backslash, followed by a unicode escape
+ doTest("//\\\\\\u000A test",
+ "END_OF_LINE_COMMENT ('//\\\\')\nWHITE_SPACE ('\\u000A ')\nIDENTIFIER ('test')"); // escaped backslash, followed by a unicode escape
}
public void testWhitespace() {