diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 0c957bda72df..94397dfe351f 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -44,7 +44,7 @@ - + @@ -187,6 +187,8 @@ implementationClass="org.jetbrains.idea.devkit.navigation.ExtensionPointDeclarationRelatedItemLineMarkerProvider"/> + + diff --git a/plugins/devkit/src/references/RegistryImplicitPropertyUsageProvider.java b/plugins/devkit/src/inspections/RegistryImplicitPropertyUsageProvider.java similarity index 64% rename from plugins/devkit/src/references/RegistryImplicitPropertyUsageProvider.java rename to plugins/devkit/src/inspections/RegistryImplicitPropertyUsageProvider.java index d40f100e4111..be8884c3b1ea 100644 --- a/plugins/devkit/src/references/RegistryImplicitPropertyUsageProvider.java +++ b/plugins/devkit/src/inspections/RegistryImplicitPropertyUsageProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -13,25 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.idea.devkit.references; +package org.jetbrains.idea.devkit.inspections; import com.intellij.codeInspection.unused.ImplicitPropertyUsageProvider; import com.intellij.lang.properties.psi.Property; -import com.intellij.psi.PsiFile; -import org.jetbrains.idea.devkit.util.PsiUtil; /** * @author Konstantin Bulenkov */ public class RegistryImplicitPropertyUsageProvider extends ImplicitPropertyUsageProvider { + @Override protected boolean isUsed(Property property) { - if (PsiUtil.isIdeaProject(property.getProject())) { - final PsiFile file = property.getContainingFile(); - if (file != null && file.getName().equals("registry.properties")) { - final String name = property.getName(); - return name.endsWith(".description") || name.endsWith(".restartRequired"); - } + if (RegistryPropertiesAnnotator.isRegistryPropertiesFile(property.getContainingFile())) { + final String name = property.getName(); + return name != null && RegistryPropertiesAnnotator.isImplicitUsageKey(name); } return false; } diff --git a/plugins/devkit/src/inspections/RegistryPropertiesAnnotator.kt b/plugins/devkit/src/inspections/RegistryPropertiesAnnotator.kt new file mode 100644 index 000000000000..b0796533f882 --- /dev/null +++ b/plugins/devkit/src/inspections/RegistryPropertiesAnnotator.kt @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2016 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.idea.devkit.inspections + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.lang.properties.psi.PropertiesFile +import com.intellij.lang.properties.psi.impl.PropertyImpl +import com.intellij.lang.properties.psi.impl.PropertyKeyImpl +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.util.IncorrectOperationException +import com.intellij.util.PsiNavigateUtil +import org.jetbrains.annotations.Nls +import org.jetbrains.annotations.NonNls +import org.jetbrains.idea.devkit.util.PsiUtil + +/** + * Highlights key in `registry.properties` without matching `key.description` entry + corresponding quickfix. + */ +class RegistryPropertiesAnnotator : Annotator { + + override fun annotate(element: PsiElement, holder: AnnotationHolder) { + if (element !is PropertyKeyImpl) return + + val file = holder.currentAnnotationSession.file + if (!isRegistryPropertiesFile(file)) { + return + } + + val propertyName = element.text + if (isImplicitUsageKey(propertyName)) { + return + } + + val propertiesFile = file as PropertiesFile + val descriptionProperty = propertiesFile.findPropertyByKey(propertyName + DESCRIPTION_SUFFIX) + if (descriptionProperty == null) { + holder.createWarningAnnotation(element.node, "Key '$propertyName' does not have description key") + .registerFix(AddDescriptionKeyIntention(propertyName)) + } + } + + private class AddDescriptionKeyIntention(private val myPropertyName: String) : IntentionAction { + + @Nls + override fun getText(): String { + return "Add description key for '$myPropertyName'" + } + + @Nls + override fun getFamilyName(): String { + return "Add description key" + } + + override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean { + return true + } + + @Throws(IncorrectOperationException::class) + override fun invoke(project: Project, editor: Editor, file: PsiFile) { + val propertiesFile = file as PropertiesFile + + val originalProperty = propertiesFile.findPropertyByKey(myPropertyName) as PropertyImpl? + val descriptionProperty = propertiesFile.addPropertyAfter(myPropertyName + DESCRIPTION_SUFFIX, "Description", originalProperty) + + val valueNode = (descriptionProperty.psiElement as PropertyImpl).valueNode!! + PsiNavigateUtil.navigate(valueNode.psi) + } + + override fun startInWriteAction(): Boolean { + return true + } + } + + companion object { + + @NonNls + private val REGISTRY_PROPERTIES_FILENAME = "registry.properties" + + @NonNls + private val DESCRIPTION_SUFFIX = ".description" + + @NonNls + private val RESTART_REQUIRED_SUFFIX = ".restartRequired" + + @JvmStatic fun isImplicitUsageKey(keyName: String): Boolean { + return StringUtil.endsWith(keyName, DESCRIPTION_SUFFIX) || StringUtil.endsWith(keyName, RESTART_REQUIRED_SUFFIX) + } + + @JvmStatic fun isRegistryPropertiesFile(psiFile: PsiFile): Boolean { + return PsiUtil.isIdeaProject(psiFile.project) && psiFile.name == REGISTRY_PROPERTIES_FILENAME + } + } +} \ No newline at end of file