RSL: suggest to remove redundant backticks (IDEA-190341)

This commit is contained in:
Anna Kozlova
2018-05-02 20:06:10 +02:00
parent 8cf92baf00
commit 454fd44b5a
6 changed files with 118 additions and 0 deletions
@@ -241,5 +241,9 @@
<syntaxHighlighter id="java.class" key="CLASS" factoryClass="com.intellij.lang.java.JavaSyntaxHighlighterFactory"/>
<lang.syntaxHighlighterFactory language="JAVA" implementationClass="com.intellij.lang.java.JavaSyntaxHighlighterFactory"/>
<moduleExtension implementation="com.intellij.openapi.roots.impl.JavaModuleExternalPathsImpl"/>
<localInspection groupPath="Java,Java language level migration aids" language="JAVA" cleanupTool="true"
enabledByDefault="true" level="WARNING" groupBundle="messages.InspectionsBundle"
groupKey="group.names.language.level.specific.issues.and.migration.aids11" bundle="messages.InspectionsBundle" shortName="RedundantBackticksAroundRawStringLiteral"
implementationClass="com.intellij.codeInspection.RedundantBackticksAroundRawStringLiteralInspection" key="redundant.backticks.around.raw.string.literal.display.name"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,57 @@
// Copyright 2000-2018 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.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
public class RedundantBackticksAroundRawStringLiteralInspection extends AbstractBaseJavaLocalInspectionTool implements CleanupLocalInspectionTool {
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
if (PsiUtil.getLanguageLevel(holder.getFile()) != LanguageLevel.JDK_11_PREVIEW) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
@Override
public void visitLiteralExpression(PsiLiteralExpression expression) {
if (((PsiLiteralExpressionImpl)expression).getLiteralElementType() == JavaTokenType.RAW_STRING_LITERAL) {
String text = expression.getText();
String rawString = ((PsiLiteralExpressionImpl)expression).getRawString();
int reducedNumberOfBackTicks = PsiRawStringLiteralUtil.getReducedNumberOfBackticks(text);
if (reducedNumberOfBackTicks > 0) {
String newBackticksSequence = StringUtil.repeat("`", reducedNumberOfBackTicks);
int redundantTicksLength = (text.length() - rawString.length()) / 2 - reducedNumberOfBackTicks;
holder.registerProblem(expression, "Number of backticks may be reduced by " + redundantTicksLength,
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL,
new TextRange(0, redundantTicksLength),
new LocalQuickFix() {
@Nls
@NotNull
@Override
public String getFamilyName() {
return "Reduce number of backticks";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
PsiExpression newRawStringLiteral = JavaPsiFacade.getElementFactory(project)
.createExpressionFromText(
newBackticksSequence + rawString + newBackticksSequence, element);
element.replace(newRawStringLiteral);
}
});
}
}
}
};
}
}
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports raw string literals when the number of backticks around the value may be reduced.
</body>
</html>
@@ -2,6 +2,7 @@
package com.intellij.psi;
import com.intellij.openapi.util.text.StringUtil;
import gnu.trove.TIntHashSet;
public class PsiRawStringLiteralUtil {
/**
@@ -48,4 +49,35 @@ public class PsiRawStringLiteralUtil {
while (quotesLength < length && literalText.charAt(quotesLength) == '`') quotesLength++;
return quotesLength - startIndex;
}
/**
* For given raw string literal text (with backticks) returns minimal number of backticks required for string content
* @return number less than current number of backticks,
* -1 otherwise
*/
public static int getReducedNumberOfBackticks(String text) {
int leadingTicsSequence = getLeadingTicsSequence(text);
int trailingTicsSequence = getTrailingTicsSequence(text);
if (leadingTicsSequence == trailingTicsSequence && leadingTicsSequence > 1) {
int length = text.length() - trailingTicsSequence;
int idx = leadingTicsSequence;
TIntHashSet usedTicSequences = new TIntHashSet();
usedTicSequences.add(leadingTicsSequence);
while (idx < length) {
idx = text.indexOf("`", idx);
if (idx < 0) break;
int ticsSequence = getTicsSequence(text, length, idx);
usedTicSequences.add(ticsSequence);
idx += ticsSequence;
}
for (int i = 1; i < leadingTicsSequence; i++) {
if (!usedTicSequences.contains(i)) {
return i;
}
}
}
return -1;
}
}
@@ -4,6 +4,7 @@ package com.intellij.java.codeInsight.daemon;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.RedundantBackticksAroundRawStringLiteralInspection;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElementFactory;
@@ -97,6 +98,23 @@ public class LightAdvRawStringLiteralsTest extends LightCodeInsightFixtureTestCa
assertEquals("abc", ((PsiLiteralExpressionImpl)rawStringLiteral).getRawString());
}
public void testReduceNumberOfBackticks() {
doTestRedundantBackticks("class A {{String s = <caret>```a`b```;}}", "class A {{String s = ``a`b``;}}");
}
public void testReduceNumberOfBackticksSimple() {
doTestRedundantBackticks("class A {{String s = <caret>```a``b```;}}", "class A {{String s = `a``b`;}}");
}
private void doTestRedundantBackticks(String beforeText, String afterText) {
myFixture.configureByText("a.java", beforeText);
myFixture.enableInspections(new RedundantBackticksAroundRawStringLiteralInspection());
IntentionAction reduceNumberOfBackticks = myFixture.getAvailableIntention("Reduce number of backticks");
assertNotNull(reduceNumberOfBackticks);
myFixture.launchAction(reduceNumberOfBackticks);
myFixture.checkResult(afterText);
}
public void testTypingOpeningTic() {
myFixture.configureByFile(getTestName(false ) + ".java");
myFixture.type('`');
@@ -577,6 +577,7 @@ group.names.language.level.specific.issues.and.migration.aids8=Java 8
group.names.language.level.specific.issues.and.migration.aids9=Java 9
group.names.language.level.specific.issues.and.migration.aids5=Java 5
group.names.language.level.specific.issues.and.migration.aids10=Java 10
group.names.language.level.specific.issues.and.migration.aids11=Java 11
group.names.javabeans.issues=JavaBeans issues
group.names.inheritance.issues=Inheritance issues
group.names.data.flow.issues=Data flow
@@ -978,3 +979,4 @@ inspection.class.getclass.display.name=Class.getClass() call
inspection.class.getclass.message='getClass()' is called on Class instance
inspection.class.getclass.fix.remove.name=Remove 'getClass()' call
inspection.class.getclass.fix.replace.name=Replace with 'Class.class'
redundant.backticks.around.raw.string.literal.display.name=Redundant backticks around raw string literal