Migrate Intentions and Inspections tutorials from Confluence and minor fixes

- https://confluence.jetbrains.com/display/IDEADEV/Creation+of+Intention+Action
-https://confluence.jetbrains.com/display/IDEADEV/Inspection+of+Code+Source
This commit is contained in:
breandan 2015-12-11 15:05:46 -05:00
parent 1793463daf
commit d30c85e66c
24 changed files with 573 additions and 0 deletions

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PLUGIN_MODULE" version="4">
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/source/META-INF/plugin.xml" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/testSource" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,14 @@
<idea-plugin version="2">
<name>Comparing References Inspection</name>
<description>Inspection for (probably) inappropriate use of equality relation operation.</description>
<version>1.0</version>
<vendor>JetBrains</vendor>
<!--
<idea-version since-build="3000"/>
-->
<extensions defaultExtensionNs="com.intellij">
<inspectionToolProvider implementation="com.intellij.codeInspection.ComparingReferencesProvider"/>
</extensions>
</idea-plugin>

View File

@ -0,0 +1,152 @@
package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.ui.DocumentAdapter;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.StringTokenizer;
/**
* @author max
*/
public class ComparingReferencesInspection extends BaseJavaLocalInspectionTool {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ComparingReferencesInspection");
private final LocalQuickFix myQuickFix = new MyQuickFix();
@SuppressWarnings({"WeakerAccess"}) @NonNls public String CHECKED_CLASSES = "java.lang.String;java.util.Date";
@NonNls private static final String DESCRIPTION_TEMPLATE = InspectionsBundle.message("inspection.comparing.references.problem.descriptor");
@NotNull
public String getDisplayName() {
return "'==' or '!=' instead of 'equals()'";
}
@NotNull
public String getGroupDisplayName() {
return GroupNames.BUGS_GROUP_NAME;
}
@NotNull
public String getShortName() {
return "ComparingReferences";
}
private boolean isCheckedType(PsiType type) {
if (!(type instanceof PsiClassType)) return false;
StringTokenizer tokenizer = new StringTokenizer(CHECKED_CLASSES, ";");
while (tokenizer.hasMoreTokens()) {
String className = tokenizer.nextToken();
if (type.equalsToText(className)) return true;
}
return false;
}
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) {
}
@Override public void visitBinaryExpression(PsiBinaryExpression expression) {
super.visitBinaryExpression(expression);
IElementType opSign = expression.getOperationTokenType();
if (opSign == JavaTokenType.EQEQ || opSign == JavaTokenType.NE) {
PsiExpression lOperand = expression.getLOperand();
PsiExpression rOperand = expression.getROperand();
if (rOperand == null || isNullLiteral(lOperand) || isNullLiteral(rOperand)) return;
PsiType lType = lOperand.getType();
PsiType rType = rOperand.getType();
if (isCheckedType(lType) || isCheckedType(rType)) {
holder.registerProblem(expression,
DESCRIPTION_TEMPLATE, myQuickFix);
}
}
}
};
}
private static boolean isNullLiteral(PsiExpression expr) {
return expr instanceof PsiLiteralExpression && "null".equals(expr.getText());
}
private static class MyQuickFix implements LocalQuickFix {
@NotNull
public String getName() {
// The test (see the TestThisPlugin class) uses this string to identify the quick fix action.
return InspectionsBundle.message("inspection.comparing.references.use.quickfix");
}
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
try {
PsiBinaryExpression binaryExpression = (PsiBinaryExpression)descriptor.getPsiElement();
IElementType opSign = binaryExpression.getOperationTokenType();
PsiExpression lExpr = binaryExpression.getLOperand();
PsiExpression rExpr = binaryExpression.getROperand();
if (rExpr == null)
return;
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiMethodCallExpression equalsCall = (PsiMethodCallExpression)factory.createExpressionFromText("a.equals(b)", null);
equalsCall.getMethodExpression().getQualifierExpression().replace(lExpr);
equalsCall.getArgumentList().getExpressions()[0].replace(rExpr);
PsiExpression result = (PsiExpression)binaryExpression.replace(equalsCall);
if (opSign == JavaTokenType.NE) {
PsiPrefixExpression negation = (PsiPrefixExpression)factory.createExpressionFromText("!a", null);
negation.getOperand().replace(result);
result.replace(negation);
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
@NotNull
public String getFamilyName() {
return getName();
}
}
public JComponent createOptionsPanel() {
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
final JTextField checkedClasses = new JTextField(CHECKED_CLASSES);
checkedClasses.getDocument().addDocumentListener(new DocumentAdapter() {
public void textChanged(DocumentEvent event) {
CHECKED_CLASSES = checkedClasses.getText();
}
});
panel.add(checkedClasses);
return panel;
}
public boolean isEnabledByDefault() {
return true;
}
}

View File

@ -0,0 +1,10 @@
package com.intellij.codeInspection;
/**
* @author max
*/
public class ComparingReferencesProvider implements InspectionToolProvider {
public Class[] getInspectionClasses() {
return new Class[] { ComparingReferencesInspection.class};
}
}

View File

@ -0,0 +1,7 @@
<html>
<body>
This inspection reports when the '==' or '!=' operator was used between expressions of
reference types. <br>
In the text field below, specify the semicolon separated list of classes to be considered as suspicious.
</body>
</html>

View File

@ -0,0 +1,6 @@
public class X {
public boolean compare2Strings(java.lang.String s1, java.lang.String s2) {
return (s1.equals(s2));
}
}

View File

@ -0,0 +1,6 @@
public class X {
public boolean compare2Strings(java.lang.String s1, java.lang.String s2) {
return (<caret>s1 == s2);
}
}

View File

@ -0,0 +1,6 @@
public class X {
public boolean compare2Dates(java.util.Date dt1, java.util.Date dt2){
return (!dt1.equals(dt2));
}
}

View File

@ -0,0 +1,6 @@
public class X {
public boolean compare2Dates(java.util.Date dt1, java.util.Date dt2){
return (dt1 <caret>!= dt2);
}
}

View File

@ -0,0 +1,67 @@
package testPlugin;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInspection.ComparingReferencesInspection;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.*;
import junit.framework.Assert;
import java.util.List;
/**
* @see JavaCodeInsightFixtureTestCase
* @see LightCodeInsightFixtureTestCase
*/
public class TestThisPlugin extends UsefulTestCase {
protected CodeInsightTestFixture myFixture;
// Specify path to your test data directory
// e.g. final String dataPath = "c:\\users\\john.doe\\idea\\community\\samples\\ComparingReferences/testData";
final String dataPath = "c:\\users\\John.Doe\\idea\\community\\samples\\comparingReferences/testData";
public void setUp() throws Exception {
final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory();
final TestFixtureBuilder<IdeaProjectTestFixture> testFixtureBuilder = fixtureFactory.createFixtureBuilder(getName());
myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(testFixtureBuilder.getFixture());
myFixture.setTestDataPath(dataPath);
final JavaModuleFixtureBuilder builder = testFixtureBuilder.addModule(JavaModuleFixtureBuilder.class);
builder.addContentRoot(myFixture.getTempDirPath()).addSourceRoot("");
builder.setMockJdkLevel(JavaModuleFixtureBuilder.MockJdkLevel.jdk15);
myFixture.setUp();
}
public void tearDown() throws Exception {
myFixture.tearDown();
myFixture = null;
}
protected void doTest(String testName, String hint) throws Throwable {
myFixture.configureByFile(testName + ".java");
myFixture.enableInspections(ComparingReferencesInspection.class);
List<HighlightInfo> highlightInfos = myFixture.doHighlighting();
Assert.assertTrue(!highlightInfos.isEmpty());
final IntentionAction action = myFixture.findSingleIntention(hint);
Assert.assertNotNull(action);
myFixture.launchAction(action);
myFixture.checkResultByFile(testName + ".after.java");
}
// Test the "==" case
public void test() throws Throwable {
doTest("before", "Use equals()");
}
// Test the "!=" case
public void test1() throws Throwable {
doTest("before1", "Use equals()");
}
}

View File

@ -0,0 +1,27 @@
<idea-plugin version="2">
<name>Conditional Operator Converter</name>
<id>ConditionalOperatorConverter</id>
<description>Intention action that suggests to convert a conditional operator into
'if' block.
</description>
<version>1.3</version>
<vendor>JetBrains</vendor>
<!--
<idea-version since-build="2000"/>
-->
<extensions defaultExtensionNs="com.intellij">
<intentionAction>
<className>com.intellij.codeInsight.intention.ConditionalOperatorConvertor</className>
<category>Conditional Operator</category>
<descriptionDirectoryName>ConditionalOperatorConvertor</descriptionDirectoryName>
</intentionAction>
</extensions>
<project-components>
<component>
<implementation-class>com.intellij.codeInsight.intention.ConditionalOperatorConvertor</implementation-class>
</component>
</project-components>
</idea-plugin>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PLUGIN_MODULE" version="4">
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/source/META-INF/plugin.xml" />
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/source" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/testSource" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,35 @@
<idea-plugin version="2">
<id>com.your.company.unique.plugin.id</id>
<name>Plugin display name here</name>
<version>1.0</version>
<vendor email="support@yourcompany.com" url="http://www.yourcompany.com">YourCompany</vendor>
<description><![CDATA[
Enter short description for your plugin here.<br>
<em>most HTML tags may be used</em>
]]></description>
<change-notes><![CDATA[
Add change notes here.<br>
<em>most HTML tags may be used</em>
]]>
</change-notes>
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for description -->
<idea-version since-build="141.0"/>
<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
<!-- uncomment to enable plugin in all products
<depends>com.intellij.modules.lang</depends>
-->
<extensions defaultExtensionNs="com.intellij">
<!-- Add your extensions here -->
</extensions>
<actions>
<!-- Add your actions here -->
</actions>
</idea-plugin>

View File

@ -0,0 +1,110 @@
package com.intellij.codeInsight.intention;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author dsl
*/
@NonNls public class ConditionalOperatorConvertor extends PsiElementBaseIntentionAction implements IntentionAction {
@NotNull
public String getText() {
return "Convert ternary operator to if statement";
}
@NotNull
public String getFamilyName() {
return getText();
}
public boolean isAvailable(@NotNull Project project, Editor editor, @Nullable PsiElement element) {
if (element == null) return false;
if (!element.isWritable()) return false;
if (element instanceof PsiJavaToken) {
final PsiJavaToken token = (PsiJavaToken)element;
if (token.getTokenType() != JavaTokenType.QUEST) return false;
if (token.getParent() instanceof PsiConditionalExpression) {
final PsiConditionalExpression conditionalExpression = (PsiConditionalExpression)token.getParent();
if (conditionalExpression.getThenExpression() == null
|| conditionalExpression.getElseExpression() == null) {
return false;
}
return true;
}
return false;
}
return false;
}
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
final int offset = editor.getCaretModel().getOffset();
final PsiElement element = file.findElementAt(offset);
PsiConditionalExpression conditionalExpression = PsiTreeUtil.getParentOfType(element,
PsiConditionalExpression.class, false);
if (conditionalExpression == null) return;
if (conditionalExpression.getThenExpression() == null || conditionalExpression.getElseExpression() == null) return;
final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiElement originalStatement = PsiTreeUtil.getParentOfType(conditionalExpression, PsiStatement.class, false);
while (originalStatement instanceof PsiForStatement) {
originalStatement = PsiTreeUtil.getParentOfType(originalStatement, PsiStatement.class, true);
}
if (originalStatement == null) return;
// Maintain declrations
if (originalStatement instanceof PsiDeclarationStatement) {
final PsiDeclarationStatement declaration = (PsiDeclarationStatement)originalStatement;
final PsiElement[] declaredElements = declaration.getDeclaredElements();
PsiLocalVariable variable = null;
for (PsiElement declaredElement : declaredElements) {
if (declaredElement instanceof PsiLocalVariable && PsiTreeUtil.isAncestor(declaredElement, conditionalExpression, true)) {
variable = (PsiLocalVariable)declaredElement;
break;
}
}
if (variable == null) return;
variable.normalizeDeclaration();
final Object marker = new Object();
PsiTreeUtil.mark(conditionalExpression, marker);
PsiExpressionStatement statement =
(PsiExpressionStatement)factory.createStatementFromText(variable.getName() + " = 0;", null);
statement = (PsiExpressionStatement)CodeStyleManager.getInstance(project).reformat(statement);
((PsiAssignmentExpression)statement.getExpression()).getRExpression().replace(variable.getInitializer());
variable.getInitializer().delete();
final PsiElement variableParent = variable.getParent();
originalStatement = variableParent.getParent().addAfter(statement, variableParent);
conditionalExpression = (PsiConditionalExpression)PsiTreeUtil.releaseMark(originalStatement, marker);
}
// create then and else branches
final PsiElement[] originalElements = new PsiElement[]{originalStatement, conditionalExpression};
final PsiExpression condition = (PsiExpression)conditionalExpression.getCondition().copy();
final PsiElement[] thenElements = PsiTreeUtil.copyElements(originalElements);
final PsiElement[] elseElements = PsiTreeUtil.copyElements(originalElements);
thenElements[1].replace(conditionalExpression.getThenExpression());
elseElements[1].replace(conditionalExpression.getElseExpression());
PsiIfStatement statement = (PsiIfStatement)factory.createStatementFromText("if (true) { a = b } else { c = d }",
null);
statement = (PsiIfStatement)CodeStyleManager.getInstance(project).reformat(statement);
statement.getCondition().replace(condition);
statement = (PsiIfStatement)originalStatement.replace(statement);
((PsiBlockStatement)statement.getThenBranch()).getCodeBlock().getStatements()[0].replace(thenElements[0]);
((PsiBlockStatement)statement.getElseBranch()).getCodeBlock().getStatements()[0].replace(elseElements[0]);
}
public boolean startInWriteAction() {
return true;
}
}

View File

@ -0,0 +1,11 @@
public class X {
void f(boolean isMale) {
String title;
if (isMale) {
title = "Mr.";
} else {
title = "Ms.";
}
System.out.println("title = " + title);
}
}

View File

@ -0,0 +1,6 @@
public class X {
void f(boolean isMale) {
String title = isMale <spot>?</spot> "Mr." : "Ms.";
System.out.println("title = " + title);
}
}

View File

@ -0,0 +1,5 @@
<html>
<body>
This intention converts a ternary operator to a corresponding if statement. <br>
</body>
</html>

View File

@ -0,0 +1,11 @@
public class X {
void f(boolean isMale) {
String title;
if (isMale) {
title = "Mr.";
} else {
title = "Ms.";
}
System.out.println("title = " + title);
}
}

View File

@ -0,0 +1,6 @@
public class X {
void f(boolean isMale) {
String title = isMale <caret>? "Mr." : "Ms.";
System.out.println("title = " + title);
}
}

View File

@ -0,0 +1,62 @@
package testPlugin;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.*;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Created by IntelliJ IDEA.
* User: Alexey.Chursin
* Date: Sep 13, 2010
* Time: 9:35:50 PM
* To change this template use File | Settings | File Templates.
*/
public class YourTest {
protected CodeInsightTestFixture myFixture;
// Specify path to your test data
// e.g. final String dataPath = "c:\\users\\john.doe\\idea\\community\\samples\\conditionalOperatorConvertor/testData";
final String dataPath = "c:\\users\\John.Doe\\idea\\community\\samples\\conditionalOperatorConvertor/testData";
@Before
public void setUp() throws Exception {
final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory();
final TestFixtureBuilder<IdeaProjectTestFixture> testFixtureBuilder = fixtureFactory.createFixtureBuilder();
myFixture = JavaTestFixtureFactory.getFixtureFactory().createCodeInsightFixture(testFixtureBuilder.getFixture());
myFixture.setTestDataPath(dataPath);
final JavaModuleFixtureBuilder builder = testFixtureBuilder.addModule(JavaModuleFixtureBuilder.class);
builder.addContentRoot(myFixture.getTempDirPath()).addSourceRoot("");
builder.setMockJdkLevel(JavaModuleFixtureBuilder.MockJdkLevel.jdk15);
myFixture.setUp();
}
@After
public void tearDown() throws Exception {
myFixture.tearDown();
myFixture = null;
}
protected void doTest(String testName, String hint) throws Throwable {
// Messages.showInfoMessage("Test started", "Info");
myFixture.configureByFile(testName + ".java");
final IntentionAction action = myFixture.findSingleIntention(hint);
Assert.assertNotNull(action);
myFixture.launchAction(action);
myFixture.checkResultByFile(testName + ".after.java");
}
@Test
public void test() throws Throwable {
doTest("before.template", "Convert ternary operator to if statement");
}
}