mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
TextBlockMigrationInspection: added inspection that reports cases when string or concatenation can be converted to text block (IDEA-217333)
GitOrigin-RevId: 50750c58f63d1289b236c612ed33e0655b66ef76
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d002bd2dab
commit
550b0aeb8b
@@ -1330,6 +1330,12 @@
|
||||
bundle="messages.InspectionsBundle"
|
||||
key="inspection.redundant.explicit.close"
|
||||
implementationClass="com.intellij.codeInspection.RedundantExplicitCloseInspection"/>
|
||||
<localInspection groupPath="Java,Java language level migration aids" language="JAVA" shortName="TextBlockMigration"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids13" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.TextBlockMigrationInspection"
|
||||
bundle="messages.InspectionsBundle"
|
||||
key="inspection.text.block.migration.name"/>
|
||||
<localInspection groupPath="Java,Java language level migration aids" language="JAVA" shortName="TextBlockBackwardMigration"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids13" enabledByDefault="true" level="INFORMATION"
|
||||
|
||||
@@ -246,7 +246,10 @@ public class StringLiteralCopyPasteProcessor implements CopyPastePreProcessor {
|
||||
StringBuilder buffer = new StringBuilder(text.length());
|
||||
final String[] lines = LineTokenizer.tokenize(text.toCharArray(), false, false);
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
buffer.append(PsiLiteralUtil.escapeTextBlockCharacters(lines[i], i == 0 && escapeStartQuote, i == lines.length - 1 && escapeEndQuote));
|
||||
String content = PsiLiteralUtil.escapeBackSlashesInTextBlock(lines[i]);
|
||||
content = PsiLiteralUtil.escapeTextBlockCharacters(content, i == 0 && escapeStartQuote,
|
||||
i == lines.length - 1 && escapeEndQuote, true);
|
||||
buffer.append(content);
|
||||
if (i < lines.length - 1) {
|
||||
buffer.append('\n');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
|
||||
import com.intellij.psi.util.PsiLiteralUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.PsiReplacementUtil;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class TextBlockMigrationInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!HighlightUtil.Feature.TEXT_BLOCKS.isAvailable(holder.getFile())) return PsiElementVisitor.EMPTY_VISITOR;
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitPolyadicExpression(PsiPolyadicExpression expression) {
|
||||
if (!isConcatenation(expression)) return;
|
||||
int newLinesCnt = 0;
|
||||
for (PsiExpression operand : expression.getOperands()) {
|
||||
String text = getExpressionText(operand, false);
|
||||
if (text == null) return;
|
||||
if (newLinesCnt <= 1) newLinesCnt += StringUtils.countMatches(text, "\n");
|
||||
}
|
||||
if (newLinesCnt <= 1) return;
|
||||
holder.registerProblem(expression, InspectionsBundle.message("inspection.text.block.migration.message", "Concatenation"),
|
||||
new ReplaceWithTextBlockFix());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLiteralExpression(PsiLiteralExpression expression) {
|
||||
String text = getExpressionText(expression, false);
|
||||
if (text == null || StringUtils.countMatches(text, "\n") <= 1) return;
|
||||
holder.registerProblem(expression, InspectionsBundle.message("inspection.text.block.migration.message", "String"),
|
||||
new ReplaceWithTextBlockFix());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class ReplaceWithTextBlockFix implements LocalQuickFix {
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return InspectionsBundle.message("inspection.replace.with.text.block.fix");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiExpression expression = PsiUtil.skipParenthesizedExprDown(tryCast(descriptor.getPsiElement(), PsiExpression.class));
|
||||
if (expression == null) return;
|
||||
Document document = PsiDocumentManager.getInstance(project).getDocument(expression.getContainingFile());
|
||||
if (document == null) return;
|
||||
int expressionOffset = expression.getTextOffset();
|
||||
int offset = expressionOffset - document.getLineStartOffset(document.getLineNumber(expressionOffset));
|
||||
PsiLiteralExpressionImpl literalExpression = tryCast(expression, PsiLiteralExpressionImpl.class);
|
||||
if (literalExpression != null && literalExpression.getLiteralElementType() == JavaTokenType.STRING_LITERAL) {
|
||||
replaceWithTextBlock(new PsiLiteralExpressionImpl[]{literalExpression}, offset, literalExpression);
|
||||
return;
|
||||
}
|
||||
PsiPolyadicExpression polyadicExpression = tryCast(expression, PsiPolyadicExpression.class);
|
||||
if (polyadicExpression == null || !isConcatenation(polyadicExpression)) return;
|
||||
replaceWithTextBlock(polyadicExpression.getOperands(), offset, polyadicExpression);
|
||||
}
|
||||
|
||||
private static void replaceWithTextBlock(@NotNull PsiExpression[] operands, int offset, @NotNull PsiExpression toReplace) {
|
||||
StringBuilder textBlock = new StringBuilder();
|
||||
String indent = Strings.repeat(" ", offset);
|
||||
textBlock.append("\"\"\"\n").append(indent);
|
||||
boolean escapeStartQuote = false;
|
||||
for (int i = 0; i < operands.length; i++) {
|
||||
PsiExpression operand = operands[i];
|
||||
String text = getExpressionText(operand, true);
|
||||
if (text == null) return;
|
||||
boolean isLastLine = i == operands.length - 1;
|
||||
text = PsiLiteralUtil.escapeTextBlockCharacters(text, escapeStartQuote, isLastLine, isLastLine);
|
||||
escapeStartQuote = text.endsWith("\"");
|
||||
textBlock.append(text.replaceAll("\n", '\n' + indent));
|
||||
}
|
||||
textBlock.append("\"\"\"");
|
||||
PsiReplacementUtil.replaceExpression(toReplace, textBlock.toString(), new CommentTracker());
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isConcatenation(@NotNull PsiPolyadicExpression expression) {
|
||||
PsiType type = expression.getType();
|
||||
return type != null && type.equalsToText(CommonClassNames.JAVA_LANG_STRING);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getExpressionText(@NotNull PsiExpression expression, boolean isRawText) {
|
||||
PsiLiteralExpressionImpl literal = tryCast(PsiUtil.skipParenthesizedExprDown(expression), PsiLiteralExpressionImpl.class);
|
||||
if (literal == null || literal.getLiteralElementType() == JavaTokenType.TEXT_BLOCK_LITERAL) return null;
|
||||
if (literal.getLiteralElementType() == JavaTokenType.STRING_LITERAL && isRawText) return literal.getInnerText();
|
||||
Object value = literal.getValue();
|
||||
return value == null ? null : value.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports cases when string or concatenation of strings can be replaced with a text block.
|
||||
<!-- tooltip end -->
|
||||
Example:
|
||||
<pre><code>
|
||||
String html = "<html>\n" +
|
||||
" <body>\n" +
|
||||
" <p>Hello, world</p>\n" +
|
||||
" </body>\n" +
|
||||
"</html>\n";
|
||||
</code></pre>
|
||||
<p>can be replaced with</p>
|
||||
<pre><code>
|
||||
String html = """
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello, world</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
</code></pre>
|
||||
<p>This inspection works if the language level is 13 Preview.</p>
|
||||
<p><small>New in 2019.3</small></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -7,7 +7,6 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiJavaToken;
|
||||
import com.intellij.psi.PsiLiteralExpression;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -153,46 +152,173 @@ public class PsiLiteralUtil {
|
||||
return (type == JavaTokenType.CHARACTER_LITERAL || type == JavaTokenType.STRING_LITERAL) && expression.getValue() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given string to text block content.
|
||||
* String is converted as a last string in a text block.
|
||||
*
|
||||
* @param s original text
|
||||
* @see #escapeTextBlockCharacters(String, boolean, boolean, boolean)
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(pure = true)
|
||||
public static String escapeTextBlockCharacters(@NotNull String s) {
|
||||
return escapeTextBlockCharacters(s, false, true);
|
||||
return escapeTextBlockCharacters(s, false, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given string to text block content.
|
||||
* <p>During conversion:</p>
|
||||
* <li>All escaped quotes are unescaped.</li>
|
||||
* <li>Every third quote is escaped. If escapeStartQuote / escapeEndQuote is set then start / end quote is also escaped.</li>
|
||||
* <li>All spaces before \n are converted to \040 escape sequence.
|
||||
* This is required since spaces in the end of the line are trimmed by default (see JEP 355).
|
||||
* If escapeSpacesInTheEnd is set, then all spaces before the end of the line are converted even if new line in the end is missing. </li>
|
||||
* <li> All new line escape sequences are interpreted. </li>
|
||||
* <li>Rest of the content is processed as is.</li>
|
||||
*
|
||||
* @param s original text
|
||||
* @param escapeStartQuote true if first quote should be escaped (e.g. when copy-pasting into text block after two quotes)
|
||||
* @param escapeEndQuote true if last quote should be escaped (e.g. inserting text into text block before closing quotes)
|
||||
* @param escapeSpacesInTheEnd true if spaces in the end of the line should be converted to \040 even if no new line in the end is present
|
||||
*/
|
||||
@NotNull
|
||||
@Contract(pure = true)
|
||||
public static String escapeTextBlockCharacters(@NotNull String s, boolean escapeStartQuote, boolean escapeEndQuote) {
|
||||
public static String escapeTextBlockCharacters(@NotNull String s, boolean escapeStartQuote,
|
||||
boolean escapeEndQuote, boolean escapeSpacesInTheEnd) {
|
||||
int i = 0;
|
||||
int length = s.length();
|
||||
if (length == 0) return s;
|
||||
StringBuilder result = new StringBuilder(length);
|
||||
int q = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '"') {
|
||||
if (escapeStartQuote && i == 0) result.append('\\');
|
||||
q++;
|
||||
while (i < length) {
|
||||
int nextIdx = parseQuotes(i, s, result, escapeStartQuote, escapeEndQuote);
|
||||
if (nextIdx != -1) {
|
||||
i = nextIdx;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
appendQuotes(q, result);
|
||||
if (c == '\\') result.append('\\');
|
||||
result.append(c);
|
||||
q = 0;
|
||||
nextIdx = parseSpaces(i, s, result, escapeSpacesInTheEnd);
|
||||
if (nextIdx != -1) {
|
||||
i = nextIdx;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
appendQuotes(q, result);
|
||||
if (escapeEndQuote && result.charAt(result.length() - 1) == '"') {
|
||||
result.insert(result.length() - 1, '\\');
|
||||
nextIdx = parseBackSlashes(i, s, result);
|
||||
if (nextIdx != -1) {
|
||||
i = nextIdx;
|
||||
continue;
|
||||
}
|
||||
result.append(s.charAt(i));
|
||||
i++;
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static void appendQuotes(int quotes, StringBuilder result) {
|
||||
int q = quotes;
|
||||
while (q > 0) {
|
||||
if (quotes >= 3) result.append('\\');
|
||||
result.append(StringUtil.repeat("\"", Math.min(q, 3)));
|
||||
q -= 3;
|
||||
private static int parseQuotes(int start, @NotNull String s, @NotNull StringBuilder result,
|
||||
boolean escapeStartQuote, boolean escapeEndQuote) {
|
||||
char c = s.charAt(start);
|
||||
if (c != '"') return -1;
|
||||
int nQuotes = 1;
|
||||
int i = start;
|
||||
while (true) {
|
||||
int nextIdx = i + 1 >= s.length() ? -1 : parseBackSlash(s, i + 1);
|
||||
if (nextIdx == -1) nextIdx = i + 1;
|
||||
if (nextIdx >= s.length() || s.charAt(nextIdx) != '"') break;
|
||||
nQuotes++;
|
||||
i = nextIdx;
|
||||
}
|
||||
for (int q = 0; q < nQuotes; q++) {
|
||||
if (q == 0 && start == 0 && escapeStartQuote ||
|
||||
q % 3 == 2 ||
|
||||
q == nQuotes - 1 && i + 1 == s.length() && escapeEndQuote) {
|
||||
result.append("\\\"");
|
||||
}
|
||||
else {
|
||||
result.append('"');
|
||||
}
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
private static int parseSpaces(int start, @NotNull String s, @NotNull StringBuilder result, boolean escapeSpacesInTheEnd) {
|
||||
char c = s.charAt(start);
|
||||
if (c != ' ') return -1;
|
||||
int i = start;
|
||||
int nSpaces = 0;
|
||||
while (i < s.length() && s.charAt(i) == ' ') {
|
||||
nSpaces++;
|
||||
i++;
|
||||
}
|
||||
if (i >= s.length() && escapeSpacesInTheEnd) {
|
||||
result.append(StringUtil.repeat("\\040", nSpaces));
|
||||
return i;
|
||||
}
|
||||
int nextIdx = i >= s.length() ? -1 : parseBackSlash(s, i);
|
||||
if (nextIdx != -1 && nextIdx < s.length() && s.charAt(nextIdx) == 'n') {
|
||||
result.append(StringUtil.repeat("\\040", nSpaces));
|
||||
return i;
|
||||
}
|
||||
result.append(StringUtil.repeatSymbol(' ', nSpaces));
|
||||
return i;
|
||||
}
|
||||
|
||||
private static int parseBackSlashes(int start, @NotNull String s, @NotNull StringBuilder result) {
|
||||
int i = parseBackSlash(s, start);
|
||||
if (i == -1) return -1;
|
||||
int prev = start;
|
||||
int nextIdx;
|
||||
int nSlashes = 1;
|
||||
while (i < s.length()) {
|
||||
nextIdx = parseBackSlash(s, i);
|
||||
if (nextIdx != -1) {
|
||||
result.append(s, prev, i);
|
||||
prev = i;
|
||||
i = nextIdx;
|
||||
nSlashes++;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= s.length()) {
|
||||
// line ends with a backslash
|
||||
result.append(s, prev, s.length());
|
||||
}
|
||||
else if (nSlashes % 2 == 0) {
|
||||
// symbol after slashes is not escaped
|
||||
result.append(s, prev, i);
|
||||
}
|
||||
else {
|
||||
// found something that is escaped with a backslash
|
||||
char next = s.charAt(i);
|
||||
if (next == 'n') {
|
||||
result.append('\n');
|
||||
}
|
||||
else if (next == '"') {
|
||||
return i;
|
||||
}
|
||||
else {
|
||||
result.append(s, prev, i).append(next);
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes backslashes in a text block (even if they're represented as an escape sequence).
|
||||
*/
|
||||
@NotNull
|
||||
public static String escapeBackSlashesInTextBlock(@NotNull String str) {
|
||||
int i = 0;
|
||||
int length = str.length();
|
||||
StringBuilder result = new StringBuilder(length);
|
||||
while (i < length) {
|
||||
int nextIdx = parseBackSlash(str, i);
|
||||
if (nextIdx != -1) {
|
||||
result.append("\\\\");
|
||||
i = nextIdx;
|
||||
}
|
||||
else {
|
||||
result.append(str.charAt(i));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class C {
|
||||
String empty = """
|
||||
\"""
|
||||
target\""\"<caret>""";
|
||||
""\"
|
||||
target""\"<caret>""";
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenation() {
|
||||
String foobarbaz = """
|
||||
foo
|
||||
bar
|
||||
baz""";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithMultipleNewLines() {
|
||||
String text = """
|
||||
This text should be on the same line as \\n this one
|
||||
foo
|
||||
bar
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithMultipleNewLines() {
|
||||
String html = """
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello, world</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithNonStrings() {
|
||||
String answer = """
|
||||
The answer to the meaning of life,
|
||||
the universe,
|
||||
and everything
|
||||
is 42
|
||||
""";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithExtraSpaces() {
|
||||
String code = """
|
||||
<html>\040\040
|
||||
<body>
|
||||
</body>
|
||||
</html>\040\040""";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithThreeQuotes() {
|
||||
String quotes = """
|
||||
this concatenation contains
|
||||
three quotes
|
||||
one after another
|
||||
"\"\"""";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void literalWithNewLine() {
|
||||
String foo = """
|
||||
foo
|
||||
bar
|
||||
baz
|
||||
""";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenation() {
|
||||
String foobarbaz = "foo\n" <caret>+
|
||||
"bar\n" +
|
||||
"baz";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithMultipleNewLines() {
|
||||
String text = "This<caret> text should be on the same line as \\n this one\n" +
|
||||
"foo\n" +
|
||||
"bar\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithMultipleNewLines() {
|
||||
String html = "<html>\n" +<caret>
|
||||
" <body>\n" +
|
||||
" <p>Hello, world</p>\n" +
|
||||
" </body>\n" +
|
||||
"</html>\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithNonStrings() {
|
||||
String answer = "T<caret>he answer to the meaning of life,\n" +
|
||||
"the universe,\n" +
|
||||
"and everything\n" +
|
||||
"is " + 42 + '\n';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// "Fix all 'Text block can be used' problems in file" "false"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithOneNewLine() {
|
||||
String code = "<<caret>html>\n" +
|
||||
"</html>";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithExtraSpaces() {
|
||||
String code = "<<caret>html> \n" +
|
||||
" <body>\n" +
|
||||
" </body>\n" +
|
||||
"</html> ";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void concatenationWithThreeQuotes() {
|
||||
String quotes = "<caret>this concatenation contains\n" +
|
||||
" three quotes\n" +
|
||||
"one after another\n" +
|
||||
"\"" +
|
||||
'"' +
|
||||
"\"";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Fix all 'Text block can be used' problems in file" "false"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void empty() {
|
||||
String empty = "<caret>";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Replace with text block" "true"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void literalWithNewLine() {
|
||||
String foo = "foo\nbar<caret>\nbaz\n";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Fix all 'Text block can be used' problems in file" "false"
|
||||
|
||||
class TextBlockMigration {
|
||||
|
||||
void literalWithNewLine() {
|
||||
String foo = "foo\nbar<caret>";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.TextBlockMigrationInspection;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @see TextBlockMigrationInspection
|
||||
*/
|
||||
public class TextBlockMigrationInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new TextBlockMigrationInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/inspection/textBlockMigration/";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LanguageLevel getLanguageLevel() {
|
||||
return LanguageLevel.JDK_13_PREVIEW;
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.psi.util;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.siyeh.ig.LightInspectionTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.intellij.psi.util.PsiLiteralUtil.escapeTextBlockCharacters;
|
||||
import static org.junit.Assert.*;
|
||||
import static com.intellij.psi.util.PsiLiteralUtil.escapeBackSlashesInTextBlock;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
@@ -15,8 +13,29 @@ public class PsiLiteralUtilTest {
|
||||
|
||||
@Test
|
||||
public void testEscapeTextBlockCharacters() {
|
||||
assertEquals("\\\"\"\"\\\"\"\"\\\"\\\"", escapeTextBlockCharacters("\"\"\"\"\"\"\"\"", false, true));
|
||||
assertEquals("\\\\", escapeTextBlockCharacters("\\", false, true));
|
||||
assertEquals("", escapeTextBlockCharacters("", false, true));
|
||||
assertEquals("foo\\040\\040\n", PsiLiteralUtil.escapeTextBlockCharacters("foo \\n"));
|
||||
// escapes after 'bar' should be escaped since it's the last line in a text block
|
||||
assertEquals("foo\\040\\040\nbar\\040\\040", PsiLiteralUtil.escapeTextBlockCharacters("foo \\nbar "));
|
||||
|
||||
assertEquals("", PsiLiteralUtil.escapeTextBlockCharacters(""));
|
||||
// last in line quote should be escaped
|
||||
assertEquals("\\\"", PsiLiteralUtil.escapeTextBlockCharacters("\""));
|
||||
assertEquals("\"\\\"", PsiLiteralUtil.escapeTextBlockCharacters("\"\""));
|
||||
// all escaped quotes should be unescaped
|
||||
assertEquals("\"\\\"", PsiLiteralUtil.escapeTextBlockCharacters("\\\"\""));
|
||||
// every third quote should be escaped
|
||||
assertEquals("\"\"\\\"\"\"\\\"\"\\\"", PsiLiteralUtil.escapeTextBlockCharacters("\"\"\"\"\"\"\"\""));
|
||||
|
||||
|
||||
// all sequences except new line should stay as is
|
||||
assertEquals("\\t\n", PsiLiteralUtil.escapeTextBlockCharacters("\\t\\n"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEscapeBackSlashesInTextBlock() {
|
||||
assertEquals("", escapeBackSlashesInTextBlock(""));
|
||||
assertEquals("\\\\", escapeBackSlashesInTextBlock("\\"));
|
||||
// backslash before quote should be preserved
|
||||
assertEquals("\\\\\"", escapeBackSlashesInTextBlock("\\\""));
|
||||
}
|
||||
}
|
||||
@@ -1005,6 +1005,10 @@ inspection.fold.expression.into.stream.fix.name=Fold expression into Stream chai
|
||||
inspection.fold.expression.into.string.display.name=Expression can be folded into 'String.join'
|
||||
inspection.fold.expression.into.string.fix.name=Fold expression into 'String.join'
|
||||
inspection.fold.expression.fix.family.name=Fold expression
|
||||
inspection.text.block.migration.name=Text block can be used
|
||||
inspection.text.block.migration.message={0} can be replaced with text block
|
||||
inspection.replace.with.text.block.fix=Replace with text block
|
||||
|
||||
inspection.text.block.backward.migration.name=Text block can be replaced with regular string literal
|
||||
inspection.text.block.backward.migration.message=Text block can be converted to regular string literal
|
||||
inspection.replace.with.regular.string.literal.fix=Replace with regular string literal
|
||||
|
||||
Reference in New Issue
Block a user