TextBlockMigrationInspection: preserve meaningful spaces at the beginning of each line (IDEA-226395)

GitOrigin-RevId: a204f736a29271ebe83fd5f5222d0b1ebd28ddbf
This commit is contained in:
Artemiy Sartakov
2019-11-08 09:06:35 +00:00
committed by intellij-monorepo-bot
parent 325f22705f
commit 7a86f1e752
6 changed files with 102 additions and 34 deletions
@@ -46,6 +46,7 @@ public class TextBlockMigrationInspection extends AbstractBaseJavaLocalInspectio
for (PsiExpression operand : operands) {
PsiLiteralExpressionImpl literal = getLiteralExpression(operand);
if (literal == null) return;
if (nNewLines > 1) continue;
String text = literal.getText();
int newLineIdx = getNewLineIndex(text, 0);
if (newLineIdx == -1) continue;
@@ -57,7 +58,6 @@ public class TextBlockMigrationInspection extends AbstractBaseJavaLocalInspectio
nNewLines++;
newLineIdx = getNewLineIndex(text, newLineIdx + 1);
}
if (nNewLines > 1) break;
}
if (nNewLines <= 1) return;
holder.registerProblem(expression, firstNewLineTextRange,
@@ -106,22 +106,55 @@ public class TextBlockMigrationInspection extends AbstractBaseJavaLocalInspectio
}
private static void replaceWithTextBlock(@NotNull PsiExpression[] operands, @NotNull PsiExpression toReplace) {
StringBuilder textBlock = new StringBuilder();
textBlock.append("\"\"\"\n");
String[] lines = getContentLines(operands);
if (lines == null) return;
String textBlock = getTextBlock(lines);
PsiReplacementUtil.replaceExpression(toReplace, textBlock, new CommentTracker());
}
@NotNull
private static String getTextBlock(@NotNull String[] lines) {
int indent = PsiLiteralUtil.getTextBlockIndent(lines);
// we need additional indent call only when significant trailing line is missing
if (indent > 0 && lines.length > 0 && lines[lines.length - 1].endsWith("\n")) indent = 0;
String content = getTextBlockContent(lines, indent);
return "\"\"\"\n" + content + "\"\"\"" + (indent > 0 ? ".indent(" + indent + ")" : "");
}
@NotNull
private static String getTextBlockContent(@NotNull String[] lines, int indent) {
StringBuilder content = new StringBuilder();
boolean escapeStartQuote = false;
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
boolean isLastLine = i == lines.length - 1;
if (indent < line.length()) line = line.substring(indent);
line = PsiLiteralUtil.escapeTextBlockCharacters(line, escapeStartQuote, isLastLine, isLastLine);
escapeStartQuote = line.endsWith("\"");
content.append(line);
}
return content.toString();
}
@Nullable
private static String[] getContentLines(@NotNull PsiExpression[] operands) {
String[] lines = new String[operands.length];
for (int i = 0; i < operands.length; i++) {
PsiExpression operand = operands[i];
PsiLiteralExpressionImpl literal = getLiteralExpression(operand);
if (literal == null) return;
String text = getLiteralText(literal);
if (text == null) return;
boolean isLastLine = i == operands.length - 1;
text = PsiLiteralUtil.escapeTextBlockCharacters(text, escapeStartQuote, isLastLine, isLastLine);
escapeStartQuote = text.endsWith("\"");
textBlock.append(text);
if (literal == null) return null;
String line = getLiteralText(literal);
if (line == null) return null;
lines[i] = line;
}
textBlock.append("\"\"\"");
PsiReplacementUtil.replaceExpression(toReplace, textBlock.toString(), new CommentTracker());
return lines;
}
@Nullable
private static String getLiteralText(@NotNull PsiLiteralExpressionImpl literal) {
if (literal.getLiteralElementType() == JavaTokenType.STRING_LITERAL) return literal.getInnerText();
Object value = literal.getValue();
return value == null ? null : value.toString();
}
}
@@ -151,11 +184,4 @@ public class TextBlockMigrationInspection extends AbstractBaseJavaLocalInspectio
if (literal == null || literal.getLiteralElementType() == JavaTokenType.TEXT_BLOCK_LITERAL) return null;
return literal;
}
@Nullable
private static String getLiteralText(@NotNull PsiLiteralExpressionImpl literal) {
if (literal.getLiteralElementType() == JavaTokenType.STRING_LITERAL) return literal.getInnerText();
Object value = literal.getValue();
return value == null ? null : value.toString();
}
}
@@ -170,7 +170,7 @@ public class PsiLiteralUtil {
* <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).
* This is required since spaces in the end of the line are trimmed by default (see JEP 368).
* 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>
@@ -372,4 +372,22 @@ public class PsiLiteralUtil {
}
return -1;
}
/**
* Determines how many whitespaces would be excluded at the beginning of each line of text block content.
* See JEP 368 for more details.
*
* @param lines text block content
*/
public static int getTextBlockIndent(@NotNull String[] lines) {
int prefix = Integer.MAX_VALUE;
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
int indent = 0;
while (indent < line.length() && Character.isWhitespace(line.charAt(indent))) indent++;
if (indent == line.length() && i < lines.length - 1) lines[i] = "";
else if (indent < prefix) prefix = indent;
}
return prefix;
}
}
@@ -158,7 +158,7 @@ public class PsiLiteralExpressionImpl
String[] lines = getTextBlockLines();
if (lines == null) return null;
int prefix = getTextBlockIndent(lines);
int prefix = PsiLiteralUtil.getTextBlockIndent(lines);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lines.length; i++) {
@@ -176,19 +176,7 @@ public class PsiLiteralExpressionImpl
public int getTextBlockIndent() {
String[] lines = getTextBlockLines();
if (lines == null) return -1;
return getTextBlockIndent(lines);
}
private static int getTextBlockIndent(String[] lines) {
int prefix = Integer.MAX_VALUE;
for (int i = 0; i < lines.length; i++) {
String line = lines[i];
int indent = 0;
while (indent < line.length() && Character.isWhitespace(line.charAt(indent))) indent++;
if (indent == line.length() && i < lines.length - 1) lines[i] = "";
else if (indent < prefix) prefix = indent;
}
return prefix;
return PsiLiteralUtil.getTextBlockIndent(lines);
}
@Nullable
@@ -0,0 +1,13 @@
// "Replace with text block" "true"
class TextBlockMigration {
void concatenationWithSpacesAtTheBeginning() {
String body = """
<body>
<p>
</p>
</body>""".indent(2);
}
}
@@ -0,0 +1,12 @@
// "Replace with text block" "true"
class TextBlockMigration {
void concatenationWithSpacesAtTheBeginning() {
String body = " <body>\n<caret>" +
" <p>\n" +
" </p>\n" +
" </body>";
}
}
@@ -0,0 +1,11 @@
// "Fix all 'Text block can be used' problems in file" "false"
class TextBlockMigration {
void concatenationWithTextBlock() {
String concat = "foo\n<caret>" + "bar\n" +
"""
baz""";
}
}