mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
RegExp: the first RegExp inspection
This commit is contained in:
@@ -26,5 +26,8 @@
|
||||
<className>org.intellij.lang.regexp.intention.CheckRegExpIntentionAction</className>
|
||||
<category>RegExp</category>
|
||||
</intentionAction>
|
||||
|
||||
<localInspection groupName="RegExp" language="RegExp" shortName="RepeatedSpace" displayName="Consecutive spaces" enabledByDefault="true"
|
||||
level="WARNING" implementationClass="org.intellij.lang.regexp.inspection.RepeatedSpaceInspection"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports multiple consecutive spaces in a RegExp.
|
||||
Because spaces are not visible by default, it can be hard to see how many spaces are required.
|
||||
The RegExp can be made more clear by replacing consecutive spaces with a single space and a counted quantifier.
|
||||
<!-- tooltip end -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.intellij.lang.regexp.inspection;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.intellij.lang.regexp.RegExpFileType;
|
||||
import org.intellij.lang.regexp.psi.*;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
public class RepeatedSpaceInspection extends LocalInspectionTool {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new RepeatedSpaceVisitor(holder);
|
||||
}
|
||||
|
||||
private static class RepeatedSpaceVisitor extends RegExpElementVisitor {
|
||||
|
||||
private final ProblemsHolder myHolder;
|
||||
private int myCount = 0;
|
||||
private RegExpChar myFirstChar = null;
|
||||
private boolean quoted = false;
|
||||
|
||||
public RepeatedSpaceVisitor(ProblemsHolder holder) {
|
||||
myHolder = holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRegExpChar(RegExpChar aChar) {
|
||||
if (!quoted && !(aChar.getParent() instanceof RegExpClass) && aChar.getType() == RegExpChar.Type.CHAR && aChar.getValue() == ' ') {
|
||||
if (myFirstChar == null) {
|
||||
myFirstChar = aChar;
|
||||
}
|
||||
myCount++;
|
||||
}
|
||||
else {
|
||||
super.visitRegExpChar(aChar);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitWhiteSpace(PsiWhiteSpace space) {
|
||||
super.visitWhiteSpace(space);
|
||||
final String text = space.getText();
|
||||
if (text.equals("\\Q")) {
|
||||
quoted = true;
|
||||
}
|
||||
else if (text.equals("\\E")) {
|
||||
quoted = false;
|
||||
}
|
||||
myFirstChar = null;
|
||||
myCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRegExpClass(RegExpClass expClass) {
|
||||
super.visitRegExpClass(expClass);
|
||||
myFirstChar = null;
|
||||
myCount = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRegExpElement(RegExpElement element) {
|
||||
super.visitRegExpElement(element);
|
||||
if (myFirstChar != null && myCount > 1) {
|
||||
final int offset = myFirstChar.getStartOffsetInParent();
|
||||
final String message = myCount + " consecutive spaces in RegExp";
|
||||
myHolder.registerProblem(myFirstChar.getParent(), new TextRange(offset, offset + myCount), message,
|
||||
new RepeatedSpaceFix(myCount));
|
||||
}
|
||||
myFirstChar = null;
|
||||
myCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static class RepeatedSpaceFix implements LocalQuickFix {
|
||||
private final int myCount;
|
||||
|
||||
public RepeatedSpaceFix(int count) {
|
||||
myCount = count;
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Replace with ' {" + myCount + "}'";
|
||||
}
|
||||
|
||||
@Nls
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Replace with space and repeated quantifier";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (!(element instanceof RegExpBranch)) {
|
||||
return;
|
||||
}
|
||||
final TextRange range = descriptor.getTextRangeInElement();
|
||||
final StringBuilder text = new StringBuilder();
|
||||
final PsiElement[] children = element.getChildren();
|
||||
boolean inserted = false;
|
||||
for (PsiElement child : children) {
|
||||
if (!range.contains(child.getStartOffsetInParent())) {
|
||||
text.append(child.getText());
|
||||
}
|
||||
else if (!inserted) {
|
||||
text.append(" {").append(range.getLength()).append('}');
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
final RegExpPattern pattern = createPatternFromText(text, element);
|
||||
final RegExpBranch branch = PsiTreeUtil.getChildOfType(pattern, RegExpBranch.class);
|
||||
assert branch != null;
|
||||
element.replace(branch);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static RegExpPattern createPatternFromText(@NotNull final CharSequence text, @NotNull final PsiElement context) {
|
||||
final PsiFileFactory factory = PsiFileFactory.getInstance(context.getProject());
|
||||
final PsiFile file = factory.createFileFromText("dummy.regexp", RegExpFileType.INSTANCE, text);
|
||||
final RegExpPattern pattern = PsiTreeUtil.getChildOfType(file, RegExpPattern.class);
|
||||
assert pattern != null;
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.intellij.lang.regexp.inspection;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.intellij.lang.regexp.RegExpFileType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Bas Leijdekkers
|
||||
*/
|
||||
public class RepeatedSpaceInspectionTest extends LightPlatformCodeInsightFixtureTestCase {
|
||||
|
||||
public void testSimple() {
|
||||
highlightTest("<warning descr=\"2 consecutive spaces in RegExp\"> </warning>");
|
||||
}
|
||||
|
||||
public void testIgnoreQuoted() {
|
||||
highlightTest("\\Q \\E");
|
||||
}
|
||||
|
||||
public void testIgnoreInClass() {
|
||||
highlightTest("[ <warning descr=\"Duplicate character ' ' inside character class\"> </warning><warning descr=\"Duplicate character ' ' inside character class\"> </warning>]");
|
||||
}
|
||||
|
||||
public void testReplacement() {
|
||||
quickfixTest(" ", " {5}", "Replace with ' {5}'");
|
||||
}
|
||||
|
||||
private void highlightTest(@Language("RegExp") String code) {
|
||||
myFixture.enableInspections(new RepeatedSpaceInspection());
|
||||
myFixture.configureByText(RegExpFileType.INSTANCE, code);
|
||||
myFixture.testHighlighting();
|
||||
}
|
||||
|
||||
private void quickfixTest(@Language("RegExp") String before, @Language("RegExp") String after, String hint) {
|
||||
myFixture.enableInspections(new RepeatedSpaceInspection());
|
||||
myFixture.configureByText(RegExpFileType.INSTANCE, before);
|
||||
final IntentionAction intention = findIntention(hint);
|
||||
assertNotNull(intention);
|
||||
myFixture.launchAction(intention);
|
||||
myFixture.checkResult(after);
|
||||
}
|
||||
|
||||
public IntentionAction findIntention(@NotNull final String hint) {
|
||||
final List<IntentionAction> intentions = myFixture.filterAvailableIntentions(hint);
|
||||
Assert.assertFalse("\"" + hint + "\" not in " + intentions, intentions.isEmpty());
|
||||
Assert.assertFalse("Too many quickfixes found for \"" + hint + "\": " + intentions + "]", intentions.size() > 1);
|
||||
return intentions.get(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user