mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 21:41:24 +07:00
[java] IDEA-349062 Allow "Join lines" for text blocks to convert to a regular string
GitOrigin-RevId: d435698b23f56ef9ce05305266118c0e75bdb9ef
This commit is contained in:
committed by
intellij-monorepo-bot
parent
40006e5d42
commit
1fd43a5df9
@@ -8,8 +8,12 @@ public final class BasicLiteralUtil {
|
||||
private BasicLiteralUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param expression text block expression to calculate indent for
|
||||
* @return the indent of text block lines; may return -1 if text block is heavily malformed
|
||||
*/
|
||||
public static int getTextBlockIndent(@NotNull PsiElement expression) {
|
||||
String[] lines = getTextBlockLines(expression);
|
||||
String[] lines = getTextBlockLines(expression.getText(), true);
|
||||
if (lines == null) return -1;
|
||||
return getTextBlockIndent(lines);
|
||||
}
|
||||
@@ -27,14 +31,26 @@ public final class BasicLiteralUtil {
|
||||
return getTextBlockLines(rawText);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param rawText text block text, including triple quotes at the start and at the end
|
||||
* @return array of textblock content lines, including indent; null if text block is malformed
|
||||
*/
|
||||
public static String @Nullable [] getTextBlockLines(String rawText) {
|
||||
if (rawText.length() < 7 || !rawText.endsWith("\"\"\"")) return null;
|
||||
return getTextBlockLines(rawText, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rawText text block text, including triple quotes at the start and at the end
|
||||
* @param skipFirstLine if true, skip invalid content in the first line after triple quotes
|
||||
* @return array of textblock content lines, including indent; null if text block is malformed
|
||||
*/
|
||||
private static String @Nullable [] getTextBlockLines(String rawText, boolean skipFirstLine) {
|
||||
if (rawText.length() < 7 || !rawText.startsWith("\"\"\"") || !rawText.endsWith("\"\"\"")) return null;
|
||||
int start = 3;
|
||||
while (true) {
|
||||
char c = rawText.charAt(start++);
|
||||
if (c == '\n') break;
|
||||
if (!isTextBlockWhiteSpace(c) || start == rawText.length()) return null;
|
||||
if (!skipFirstLine && !isTextBlockWhiteSpace(c) || start == rawText.length()) return null;
|
||||
}
|
||||
return rawText.substring(start, rawText.length() - 3).split("\n", -1);
|
||||
}
|
||||
|
||||
@@ -7,10 +7,15 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiFragmentImpl;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiLiteralUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class TextBlockJoinLinesHandler implements JoinRawLinesHandlerDelegate {
|
||||
private static final Pattern TEXT_BLOCK_START = Pattern.compile("^\"\"\"[ \t\f]*\n", Pattern.MULTILINE);
|
||||
|
||||
@Override
|
||||
public int tryJoinRawLines(@NotNull Document doc, @NotNull PsiFile file, int start, int endWithSpaces) {
|
||||
CharSequence text = doc.getCharsSequence();
|
||||
@@ -26,10 +31,14 @@ public final class TextBlockJoinLinesHandler implements JoinRawLinesHandlerDeleg
|
||||
!tokenType.equals(JavaTokenType.TEXT_BLOCK_TEMPLATE_MID)) {
|
||||
return CANNOT_JOIN;
|
||||
}
|
||||
TextRange tokenRange = token.getTextRange();
|
||||
int lineNumber = doc.getLineNumber(start);
|
||||
boolean atStartLine = (tokenType.equals(JavaTokenType.TEXT_BLOCK_LITERAL) || tokenType.equals(JavaTokenType.TEXT_BLOCK_TEMPLATE_BEGIN))
|
||||
&& lineNumber == doc.getLineNumber(tokenRange.getStartOffset());
|
||||
boolean atEmptyStartLine = atStartLine && TEXT_BLOCK_START.matcher(token.getText()).find();
|
||||
boolean singleSlash = false;
|
||||
if (text.charAt(start) == '\\') {
|
||||
int lineNumber = doc.getLineNumber(start);
|
||||
int startOffset = Math.max(token.getTextRange().getStartOffset(), doc.getLineStartOffset(lineNumber));
|
||||
int startOffset = Math.max(tokenRange.getStartOffset(), doc.getLineStartOffset(lineNumber));
|
||||
String substring = doc.getText(TextRange.create(startOffset, start)) + "\\\n";
|
||||
CharSequence parsed = CodeInsightUtilCore.parseStringCharacters(substring, null);
|
||||
singleSlash = parsed != null && parsed.charAt(parsed.length() - 1) != '\n';
|
||||
@@ -44,14 +53,51 @@ public final class TextBlockJoinLinesHandler implements JoinRawLinesHandlerDeleg
|
||||
indent--;
|
||||
end++;
|
||||
}
|
||||
if (singleSlash) {
|
||||
boolean fromStartTillEnd = atStartLine && tokenType.equals(JavaTokenType.TEXT_BLOCK_LITERAL) &&
|
||||
doc.getLineNumber(tokenRange.getEndOffset()) == lineNumber + 1 &&
|
||||
token.getText().endsWith("\"\"\"");
|
||||
if (singleSlash || atEmptyStartLine) {
|
||||
doc.deleteString(start, end);
|
||||
end = start;
|
||||
} else {
|
||||
doc.replaceString(start, end, "\\n");
|
||||
end = start + 2;
|
||||
}
|
||||
if (fromStartTillEnd) {
|
||||
doc.replaceString(tokenRange.getStartOffset(), end + 3,
|
||||
convertToRegular(doc.getText().substring(tokenRange.getStartOffset(), end + 3)));
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
private static @NotNull String convertToRegular(@NotNull String literal) {
|
||||
if (literal.length() < 6 || !literal.startsWith("\"\"\"") || !literal.endsWith("\"\"\"")) {
|
||||
return literal;
|
||||
}
|
||||
int end = literal.length() - 3;
|
||||
StringBuilder sb = null;
|
||||
for (int i = 3; i < end; i++) {
|
||||
char ch = literal.charAt(i);
|
||||
if (ch == '"') {
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder(literal.substring(3, i));
|
||||
}
|
||||
sb.append("\\\"");
|
||||
} else if (sb != null) {
|
||||
sb.append(ch);
|
||||
}
|
||||
int nextI = PsiLiteralUtil.parseBackSlash(literal, i);
|
||||
if (nextI != -1) {
|
||||
if (sb != null) {
|
||||
sb.append(literal, i + 1, nextI + 1);
|
||||
}
|
||||
//noinspection AssignmentToForLoopParameter
|
||||
i = nextI;
|
||||
}
|
||||
}
|
||||
return sb == null ? literal.substring(2, end + 1) : '"' + sb.toString() + '"';
|
||||
}
|
||||
|
||||
private static int getNextLineStart(int start, CharSequence text) {
|
||||
int end = start;
|
||||
while (text.charAt(end) != '\n') {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = """<caret>
|
||||
Line1
|
||||
Line2
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = <selection>"""
|
||||
Line1
|
||||
Line2
|
||||
"""</selection>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = <selection>"""
|
||||
Line1
|
||||
Line2
|
||||
Line3\
|
||||
ContinueLine3
|
||||
"Quoted"
|
||||
\040""\"TripleQuoted"\""
|
||||
"""</selection>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = "Line1\nLine2\nLine3ContinueLine3\n\"Quoted\"\n\040\"\"\"TripleQuoted\"\"\"\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = "Line1\nLine2\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = <caret>"""Line1\nLine2
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = "Line1\nLine2\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = """<caret>Line0
|
||||
Line1
|
||||
Line2
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = """Line0<caret>\nLine1
|
||||
Line2
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
void test() {
|
||||
String s = """<caret>Line1
|
||||
Line2
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ class BadTextBlock {
|
||||
|
||||
void x() {
|
||||
String s = <error descr="Illegal text block start: missing new line after opening quotes">"""</error>a
|
||||
bad bad bad
|
||||
bad bad bad<warning descr="Trailing whitespace characters inside text block"> </warning>
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -299,6 +299,11 @@ public class JoinLinesTest extends LightJavaCodeInsightTestCase {
|
||||
public void testCaseLabels3() {doTest();}
|
||||
|
||||
public void testJoinTextBlock() {doTest();}
|
||||
public void testJoinTextBlockAtStartLine() {doTest();}
|
||||
public void testJoinTextBlockAtStartLineNonEmpty() {doTest();}
|
||||
public void testJoinTextBlockAtStartLineFinalStep() {doTest();}
|
||||
public void testJoinTextBlockAtStartLineComplete() {doTest();}
|
||||
public void testJoinTextBlockAtStartLineCompleteWithEscapes() {doTest();}
|
||||
public void testJoinTextBlockBackSlash() {doTest();}
|
||||
public void testJoinTextBlockBackSlash2() {doTest();}
|
||||
public void testJoinTextBlockBackDoubleSlash() {doTest();}
|
||||
|
||||
Reference in New Issue
Block a user