DevKit: registry.properties highlight keys without corresponding ".description" + quick fix

This commit is contained in:
Yann Cébron
2016-05-26 14:49:49 +02:00
parent be8dbe4122
commit c8a8807de8
3 changed files with 121 additions and 11 deletions
+3 -1
View File
@@ -44,7 +44,7 @@
<dom.implementation interfaceClass="org.jetbrains.idea.devkit.dom.Extensions"
implementationClass="org.jetbrains.idea.devkit.dom.impl.ExtensionsImpl"/>
<useScopeEnlarger implementation="org.jetbrains.idea.devkit.DevKitUseScopeEnlarger"/>
<properties.implicitPropertyUsageProvider implementation="org.jetbrains.idea.devkit.references.RegistryImplicitPropertyUsageProvider"/>
<properties.implicitPropertyUsageProvider implementation="org.jetbrains.idea.devkit.inspections.RegistryImplicitPropertyUsageProvider"/>
<configurationType implementation="org.jetbrains.idea.devkit.run.PluginConfigurationType"/>
<moduleType id="PLUGIN_MODULE" implementationClass="org.jetbrains.idea.devkit.module.PluginModuleType" classpathProvider="true"/>
@@ -187,6 +187,8 @@
implementationClass="org.jetbrains.idea.devkit.navigation.ExtensionPointDeclarationRelatedItemLineMarkerProvider"/>
<codeInsight.implementedAtRuntime implementation="org.jetbrains.idea.devkit.inspections.DevKitImplementedAtRuntimeCondition"/>
<annotator language="Properties" implementationClass="org.jetbrains.idea.devkit.inspections.RegistryPropertiesAnnotator"/>
<moduleService serviceImplementation="org.jetbrains.idea.devkit.build.PluginBuildConfiguration"/>
<generatedSourcesFilter implementation="org.jetbrains.idea.devkit.internal.IconsGeneratedSourcesFilter"/>
</extensions>
@@ -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;
}
@@ -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
}
}
}