diff --git a/platform/util/src/com/intellij/openapi/util/text/InjectorMatchingEndFinder.java b/platform/util/src/com/intellij/openapi/util/text/InjectorMatchingEndFinder.java index 41fd42bdd415..db8d1e37bdc7 100644 --- a/platform/util/src/com/intellij/openapi/util/text/InjectorMatchingEndFinder.java +++ b/platform/util/src/com/intellij/openapi/util/text/InjectorMatchingEndFinder.java @@ -17,6 +17,7 @@ package com.intellij.openapi.util.text; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static java.lang.Math.min; @@ -27,9 +28,43 @@ public class InjectorMatchingEndFinder { public static int findMatchingEnd(@NotNull String startSymbol, @NotNull String endSymbol, @NotNull String text, int afterStartIdx) { if (afterStartIdx < 0) return -1; + final Character shortStartSymbol = defineShortSymbol(startSymbol); + final Character shortEndSymbol = defineShortSymbol(endSymbol); + + if (shortStartSymbol != null || shortEndSymbol != null) { + return findForShortSymbols(nonNullifyShortSymbol(shortStartSymbol, startSymbol), + nonNullifyShortSymbol(shortEndSymbol, endSymbol), + text, afterStartIdx, endSymbol); + } return findForLongSymbols(text, afterStartIdx, startSymbol, endSymbol); } + private static char nonNullifyShortSymbol(@Nullable Character shortStartSymbol, @NotNull String startSymbol) { + return shortStartSymbol == null ? startSymbol.charAt(0) : shortStartSymbol; + } + + private static Character defineShortSymbol(final String s) { + if (s.length() == 2 && s.charAt(0) == s.charAt(1)) return s.charAt(0); + return null; + } + + private static int findForShortSymbols(char shortStartSymbol, char shortEndSymbol, @NotNull final String text, + int afterStartIdx, final @NotNull String endSymbol) { + int totalNumStarts = 1; + int lookFrom = afterStartIdx; + while (totalNumStarts > 0) { + --totalNumStarts; + int nextEndIdx = text.indexOf(endSymbol, lookFrom); + if (nextEndIdx == -1) return -1; + final int numStarts = getOccurrenceCount(text, lookFrom, nextEndIdx, shortStartSymbol); + final int numEnds = getOccurrenceCount(text, lookFrom, nextEndIdx, shortEndSymbol); + totalNumStarts += numStarts - numEnds; + lookFrom = nextEndIdx + 1; + if (totalNumStarts <= 0) return nextEndIdx; + } + return -1; + } + private static int findForLongSymbols(@NotNull final String text, int afterStartIdx, final @NotNull String startSymbol, final @NotNull String endSymbol) { int totalNumStarts = 1; @@ -48,6 +83,24 @@ public class InjectorMatchingEndFinder { return -1; } + @Contract(pure = true) + private static int getOccurrenceCount(@NotNull String text, final int from, final int toExcluding, final char c) { + int res = 0; + int i = from; + final int limit = min(text.length(), toExcluding); + while (i < limit) { + i = text.indexOf(c, i); + if (i >= 0 && i < limit) { + res++; + i++; + } + else { + break; + } + } + return res; + } + @Contract(pure = true) private static int getOccurrenceCount(@NotNull String text, final int from, final int toExcluding, final String s) { int res = 0;