diff --git a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt index f1e3925e7ec..8f0f6298bdd 100644 --- a/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/jet/generators/tests/GenerateTests.kt @@ -101,6 +101,7 @@ import org.jetbrains.jet.plugin.libraries.AbstractDecompiledTextTest import org.jetbrains.jet.plugin.imports.AbstractOptimizeImportsTest import org.jetbrains.jet.plugin.debugger.AbstractSmartStepIntoTest import org.jetbrains.jet.plugin.stubs.AbstractStubBuilderTest +import org.jetbrains.jet.plugin.codeInsight.AbstractJetInspectionTest fun main(args: Array) { System.setProperty("java.awt.headless", "true") @@ -379,6 +380,10 @@ fun main(args: Array) { model("intentions/attributeCallReplacements/replaceUnaryPrefixIntention", testMethod = "doTestReplaceUnaryPrefixIntention") model("intentions/attributeCallReplacements/replaceInvokeIntention", testMethod = "doTestReplaceInvokeIntention") model("intentions/simplifyNegatedBinaryExpressionIntention", testMethod = "doTestSimplifyNegatedBinaryExpressionIntention") + } + + testClass(javaClass()) { + model("codeInsight/inspections", extension = null, recursive = false) model("intentions/convertNegatedBooleanSequence", testMethod="doTestConvertNegatedBooleanSequence") model("intentions/convertNegatedExpressionWithDemorgansLaw", testMethod = "doTestConvertNegatedExpressionWithDemorgansLaw") model("intentions/swapBinaryExpression", testMethod = "doTestSwapBinaryExpression") diff --git a/idea/src/org/jetbrains/jet/plugin/inspections/AbstractKotlinInspection.kt b/idea/src/org/jetbrains/jet/plugin/inspections/AbstractKotlinInspection.kt new file mode 100644 index 00000000000..707647f0aa6 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/inspections/AbstractKotlinInspection.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2014 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.jetbrains.jet.plugin.inspections + +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.CustomSuppressableInspectionTool +import com.intellij.psi.PsiElement +import com.intellij.codeInspection.SuppressIntentionAction +import com.intellij.codeInspection.SuppressManager +import com.intellij.codeInsight.daemon.HighlightDisplayKey + +public abstract class AbstractKotlinInspection: LocalInspectionTool(), CustomSuppressableInspectionTool { + public override fun getSuppressActions(element: PsiElement?): Array? { + return SuppressManager.getInstance()!!.createSuppressActions(HighlightDisplayKey.find(getShortName())!!) + } + + public override fun isSuppressedFor(element: PsiElement): Boolean { + return SuppressManager.getInstance()!!.isSuppressedFor(element, getID()) + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/inspections/IntentionBasedInspection.kt b/idea/src/org/jetbrains/jet/plugin/inspections/IntentionBasedInspection.kt new file mode 100644 index 00000000000..4802df3181b --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/inspections/IntentionBasedInspection.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2010-2014 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.jetbrains.jet.plugin.inspections + +import com.intellij.psi.PsiElement +import org.jetbrains.jet.lang.psi.JetElement +import com.intellij.psi.PsiElementVisitor +import com.intellij.codeInspection.LocalInspectionToolSession +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.openapi.project.Project +import com.intellij.codeInspection.ProblemDescriptor +import org.jetbrains.jet.plugin.intentions.JetSelfTargetingIntention +import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.editor.EditorFactory +import com.intellij.openapi.editor.Editor + +public abstract class IntentionBasedInspection( + protected val intention: JetSelfTargetingIntention +) : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { + return object: PsiElementVisitor() { + override fun visitElement(element: PsiElement?) { + if (!intention.elementType.isInstance(element)) return + + [suppress("UNCHECKED_CAST")] + val targetElement = element as T + + if (!intention.isApplicableTo(targetElement)) return + + val fix = object: LocalQuickFix { + override fun getFamilyName(): String { + return getName() + } + + override fun getName(): String { + return intention.getText() + } + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + targetElement.getOrCreateEditor()?.let { editor -> + editor.getCaretModel().moveToOffset(targetElement.getTextOffset()) + intention.applyTo(targetElement, editor) + } + } + } + + holder.registerProblem(targetElement, intention.getText(), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fix) + } + } + } +} + +private fun PsiElement.getOrCreateEditor(): Editor? { + val file = getContainingFile()?.getVirtualFile() + if (file == null) return null + + val document = FileDocumentManager.getInstance()!!.getDocument(file) + if (document == null) return null + + val editorFactory = EditorFactory.getInstance()!! + + val editors = editorFactory.getEditors(document) + return if (editors.isEmpty()) editorFactory.createEditor(document) else editors[0] +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/intentions/JetSelfTargetingIntention.kt b/idea/src/org/jetbrains/jet/plugin/intentions/JetSelfTargetingIntention.kt index 0f8eb31abb5..f642a2ec22c 100644 --- a/idea/src/org/jetbrains/jet/plugin/intentions/JetSelfTargetingIntention.kt +++ b/idea/src/org/jetbrains/jet/plugin/intentions/JetSelfTargetingIntention.kt @@ -29,9 +29,9 @@ public abstract class JetSelfTargetingIntention(val key: String, setText(JetBundle.message(key)) } - protected abstract fun isApplicableTo(element: T): Boolean - protected open fun isApplicableTo(element: T, editor: Editor): Boolean = isApplicableTo(element) - protected abstract fun applyTo(element: T, editor: Editor) + abstract fun isApplicableTo(element: T): Boolean + open fun isApplicableTo(element: T, editor: Editor): Boolean = isApplicableTo(element) + abstract fun applyTo(element: T, editor: Editor) protected fun getTarget(editor: Editor, file: PsiFile): T? { val offset = editor.getCaretModel().getOffset() diff --git a/idea/tests/org/jetbrains/jet/plugin/codeInsight/AbstractJetInspectionTest.kt b/idea/tests/org/jetbrains/jet/plugin/codeInsight/AbstractJetInspectionTest.kt new file mode 100644 index 00000000000..57272cbf0b1 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/codeInsight/AbstractJetInspectionTest.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2010-2014 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.jetbrains.jet.plugin.codeInsight + +import java.io.File +import org.jetbrains.jet.InTextDirectivesUtils +import com.intellij.openapi.util.io.FileUtil +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase +import com.intellij.testFramework.LightProjectDescriptor +import org.jetbrains.jet.plugin.JetLightProjectDescriptor +import org.jetbrains.jet.plugin.PluginTestCaseBase +import com.intellij.codeInspection.InspectionEP +import com.intellij.codeInspection.ex.LocalInspectionToolWrapper + +public abstract class AbstractJetInspectionTest: LightCodeInsightFixtureTestCase() { + override fun getProjectDescriptor(): LightProjectDescriptor = JetLightProjectDescriptor.INSTANCE + + override fun setUp() { + super.setUp() + myFixture!!.setTestDataPath("${PluginTestCaseBase.getTestDataPathBase()}/codeInsight/inspections") + } + + protected fun doTest(path: String) { + val testDir = File(path).getName() + + val options = FileUtil.loadFile(File(path, "options.test"), true) + val inspectionClass = Class.forName(InTextDirectivesUtils.findStringWithPrefixes(options, "// INSPECTION_CLASS: ")!!) + + myFixture!!.testInspection(testDir, LocalInspectionToolWrapper(inspectionClass.newInstance() as LocalInspectionTool)) + } +}