mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
153 lines
5.9 KiB
Java
153 lines
5.9 KiB
Java
// Copyright 2000-2020 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.intellij.application.options.CodeStyle;
|
|
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
|
|
import com.intellij.java.JavaBundle;
|
|
import com.intellij.lang.java.JavaLanguage;
|
|
import com.intellij.openapi.project.Project;
|
|
import com.intellij.openapi.util.text.StringUtil;
|
|
import com.intellij.psi.*;
|
|
import com.intellij.psi.codeStyle.CodeStyleManager;
|
|
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
|
import com.intellij.psi.util.PsiLiteralUtil;
|
|
import com.siyeh.ig.psiutils.CommentTracker;
|
|
import org.jetbrains.annotations.Nls;
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import java.util.StringJoiner;
|
|
|
|
import static com.intellij.util.ObjectUtils.tryCast;
|
|
|
|
public class TextBlockBackwardMigrationInspection extends AbstractBaseJavaLocalInspectionTool {
|
|
|
|
@NotNull
|
|
@Override
|
|
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
|
if (!HighlightingFeature.TEXT_BLOCKS.isAvailable(holder.getFile())) return PsiElementVisitor.EMPTY_VISITOR;
|
|
return new JavaElementVisitor() {
|
|
@Override
|
|
public void visitLiteralExpression(PsiLiteralExpression expression) {
|
|
if (!expression.isTextBlock()|| PsiLiteralUtil.getTextBlockText(expression) == null) {
|
|
return;
|
|
}
|
|
holder.registerProblem(expression, JavaBundle.message("inspection.text.block.backward.migration.message"),
|
|
new ReplaceWithRegularStringLiteralFix());
|
|
}
|
|
};
|
|
}
|
|
|
|
private static class ReplaceWithRegularStringLiteralFix implements LocalQuickFix {
|
|
|
|
@Nls(capitalization = Nls.Capitalization.Sentence)
|
|
@NotNull
|
|
@Override
|
|
public String getFamilyName() {
|
|
return JavaBundle.message("inspection.replace.with.regular.string.literal.fix");
|
|
}
|
|
|
|
@Override
|
|
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
|
PsiLiteralExpression literalExpression = tryCast(descriptor.getPsiElement(), PsiLiteralExpression.class);
|
|
if (literalExpression == null || !literalExpression.isTextBlock()) return;
|
|
String text = PsiLiteralUtil.getTextBlockText(literalExpression);
|
|
if (text == null) return;
|
|
String replacement = convertToConcatenation(text);
|
|
PsiFile file = descriptor.getPsiElement().getContainingFile();
|
|
if (file == null) return;
|
|
CodeStyleSettings tempSettings = CodeStyle.getSettings(file);
|
|
tempSettings.getCommonSettings(JavaLanguage.INSTANCE).ALIGN_MULTILINE_BINARY_OPERATION = true;
|
|
CodeStyleManager manager = CodeStyleManager.getInstance(literalExpression.getProject());
|
|
CodeStyle.doWithTemporarySettings(project, tempSettings, () -> {
|
|
PsiElement result = new CommentTracker().replaceAndRestoreComments(literalExpression, replacement);
|
|
manager.reformat(result);
|
|
});
|
|
}
|
|
|
|
@NotNull
|
|
private static String convertToConcatenation(@NotNull String text) {
|
|
if (text.isEmpty()) return "\"\"";
|
|
StringJoiner joiner = new StringJoiner(" +\n");
|
|
String[] lines = getTextBlockLines(text).split("\n", -1);
|
|
for (int i = 0; i < lines.length; i++) {
|
|
String line = lines[i];
|
|
boolean addNewLine = i != lines.length - 1;
|
|
if (!addNewLine && line.isEmpty()) break;
|
|
joiner.add("\"" + line + (addNewLine ? "\\n\"" : "\""));
|
|
}
|
|
return joiner.toString();
|
|
}
|
|
|
|
@NotNull
|
|
private static String getTextBlockLines(@NotNull String text) {
|
|
int length = text.length();
|
|
StringBuilder result = new StringBuilder(length);
|
|
int i = 0;
|
|
while (i < length) {
|
|
int nSlashes = 0;
|
|
int next = i;
|
|
while (next < length && (next = PsiLiteralUtil.parseBackSlash(text, next)) != -1) {
|
|
nSlashes++;
|
|
i = next;
|
|
}
|
|
if (i >= length) {
|
|
result.append(StringUtil.repeatSymbol('\\', nSlashes));
|
|
break;
|
|
}
|
|
next = parseQuote(i, text, nSlashes, result);
|
|
if (next != -1) {
|
|
i = next;
|
|
continue;
|
|
}
|
|
if (nSlashes != 0) {
|
|
i = parseEscapedChar(i, text, nSlashes, result);
|
|
}
|
|
else {
|
|
result.append(text.charAt(i));
|
|
i++;
|
|
}
|
|
}
|
|
return result.toString();
|
|
}
|
|
|
|
private static int parseEscapedChar(int i, @NotNull String text, int nSlashes, @NotNull StringBuilder result) {
|
|
int next = parseEscapedSpace(i, text, nSlashes, result);
|
|
if (next != -1) return next;
|
|
next = parseEscapedLineBreak(i, text, nSlashes, result);
|
|
if (next != -1) return next;
|
|
result.append(StringUtil.repeatSymbol('\\', nSlashes)).append(text.charAt(i));
|
|
return i + 1;
|
|
}
|
|
|
|
private static int parseEscapedSpace(int i, @NotNull String text, int nSlashes, @NotNull StringBuilder result) {
|
|
char c = text.charAt(i);
|
|
if (c == 's' && nSlashes % 2 != 0) {
|
|
result.append(StringUtil.repeatSymbol('\\', nSlashes - 1)).append(' ');
|
|
return i + 1;
|
|
}
|
|
if (StringUtil.startsWith(text, i, "040") && nSlashes % 2 != 0) {
|
|
result.append(StringUtil.repeatSymbol('\\', nSlashes - 1)).append(' ');
|
|
return i + 3;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private static int parseEscapedLineBreak(int i, @NotNull String text, int nSlashes, @NotNull StringBuilder result) {
|
|
char c = text.charAt(i);
|
|
if (c == '\n' && nSlashes % 2 != 0) {
|
|
result.append(StringUtil.repeatSymbol('\\', nSlashes - 1));
|
|
return i + 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private static int parseQuote(int i, @NotNull String text, int nSlashes, @NotNull StringBuilder result) {
|
|
char c = text.charAt(i);
|
|
if (c != '"') return -1;
|
|
if (nSlashes % 2 == 0) nSlashes++;
|
|
result.append(StringUtil.repeatSymbol('\\', nSlashes)).append(c);
|
|
return i + 1;
|
|
}
|
|
}
|
|
}
|