diff --git a/plugins/devkit/resources/inspectionDescriptions/DescriptionNotFoundInspection.html b/plugins/devkit/resources/inspectionDescriptions/InspectionDescriptionNotFoundInspection.html similarity index 94% rename from plugins/devkit/resources/inspectionDescriptions/DescriptionNotFoundInspection.html rename to plugins/devkit/resources/inspectionDescriptions/InspectionDescriptionNotFoundInspection.html index 84653b4ffbdd..b6f17e3a20e4 100644 --- a/plugins/devkit/resources/inspectionDescriptions/DescriptionNotFoundInspection.html +++ b/plugins/devkit/resources/inspectionDescriptions/InspectionDescriptionNotFoundInspection.html @@ -2,4 +2,4 @@ This inspection detects missing html-description for an inspection. - \ No newline at end of file + diff --git a/plugins/devkit/src/DevKitInspectionToolProvider.java b/plugins/devkit/src/DevKitInspectionToolProvider.java index 911254af1f90..8bd2c2a6f1c6 100644 --- a/plugins/devkit/src/DevKitInspectionToolProvider.java +++ b/plugins/devkit/src/DevKitInspectionToolProvider.java @@ -17,18 +17,22 @@ package org.jetbrains.idea.devkit; import com.intellij.codeInspection.InspectionToolProvider; import org.jetbrains.idea.devkit.inspections.ComponentNotRegisteredInspection; -import org.jetbrains.idea.devkit.inspections.DescriptionNotFoundInspection; +import org.jetbrains.idea.devkit.inspections.InspectionDescriptionNotFoundInspection; +import org.jetbrains.idea.devkit.inspections.IntentionDescriptionNotFoundInspection; import org.jetbrains.idea.devkit.inspections.PluginXmlDomInspection; +/** + * @author Konstantin Bulenkov + */ public class DevKitInspectionToolProvider implements InspectionToolProvider { - public Class[] getInspectionClasses() { return new Class[] { //RegistrationProblemsInspection.class, PluginXmlDomInspection.class, ComponentNotRegisteredInspection.class, - DescriptionNotFoundInspection.class + InspectionDescriptionNotFoundInspection.class, + IntentionDescriptionNotFoundInspection.class }; } -} \ No newline at end of file +} diff --git a/plugins/devkit/src/inspections/DescriptionNotFoundInspection.java b/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java similarity index 97% rename from plugins/devkit/src/inspections/DescriptionNotFoundInspection.java rename to plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java index f72017c26dd7..ecfa584604e5 100644 --- a/plugins/devkit/src/inspections/DescriptionNotFoundInspection.java +++ b/plugins/devkit/src/inspections/InspectionDescriptionNotFoundInspection.java @@ -41,7 +41,7 @@ import java.util.List; /** * @author Konstantin Bulenkov */ -public class DescriptionNotFoundInspection extends DevKitInspectionBase{ +public class InspectionDescriptionNotFoundInspection extends DevKitInspectionBase{ @NonNls private static final String INSPECTION_PROFILE_ENTRY = "com.intellij.codeInspection.InspectionProfileEntry"; @NonNls private static final String INSPECTION_DESCRIPTIONS = "inspectionDescriptions"; @@ -77,7 +77,7 @@ public class DescriptionNotFoundInspection extends DevKitInspectionBase{ final PsiElement problem = getProblemElement(aClass, method); final ProblemDescriptor problemDescriptor = manager .createProblemDescriptor(problem == null ? nameIdentifier : problem, - "Inspection does not have a description", isOnTheFly, new LocalQuickFix[]{new CreateHtmlDescriptionFix(filename, module)}, + "Inspection does not have a description", isOnTheFly, new LocalQuickFix[]{new CreateHtmlDescriptionFix(filename, module, false)}, ProblemHighlightType.GENERIC_ERROR_OR_WARNING); return new ProblemDescriptor[]{problemDescriptor}; } @@ -152,7 +152,7 @@ public class DescriptionNotFoundInspection extends DevKitInspectionBase{ @NotNull public String getShortName() { - return "DescriptionNotFoundInspection"; + return "InspectionDescriptionNotFoundInspection"; } @Override diff --git a/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java b/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java new file mode 100644 index 000000000000..3bb28208ba2c --- /dev/null +++ b/plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java @@ -0,0 +1,130 @@ +/* + * Copyright 2000-2010 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.codeInspection.InspectionManager; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.devkit.inspections.quickfix.CreateHtmlDescriptionFix; +import org.jetbrains.idea.devkit.util.PsiUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * @author Konstantin Bulenkov + */ +public class IntentionDescriptionNotFoundInspection extends DevKitInspectionBase{ + @NonNls private static final String INTENTION = "com.intellij.codeInsight.intention.IntentionAction"; + @NonNls private static final String INSPECTION_DESCRIPTIONS = "intentionDescriptions"; + + @Override + public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) { + final Project project = aClass.getProject(); + final PsiIdentifier nameIdentifier = aClass.getNameIdentifier(); + final Module module = ModuleUtil.findModuleForPsiElement(aClass); + + if (nameIdentifier == null || module == null || !PsiUtil.isInstanciatable(aClass)) return null; + + final PsiClass base = JavaPsiFacade.getInstance(project).findClass(INTENTION, GlobalSearchScope.allScope(project)); + + if (base == null || ! aClass.isInheritor(base, true)) return null; + + final PsiMethod method = findNearestMethod("getFamilyName", aClass); + if (method == null) return null; + final String filename = PsiUtil.getReturnedLiteral(method, aClass); + if (filename == null) return null; + + for (PsiDirectory description : getIntentionDescriptionsDirs(module)) { + PsiDirectory dir = description.findSubdirectory(filename); + if (dir == null) dir = description.findSubdirectory(aClass.getName()); + if (dir == null) continue; + final PsiFile descr = dir.findFile("description.html"); + if (descr != null) return null; + } + + + final PsiElement problem = aClass.getNameIdentifier(); + final ProblemDescriptor problemDescriptor = manager + .createProblemDescriptor(problem == null ? nameIdentifier : problem, + "Intention does not have a description", isOnTheFly, new LocalQuickFix[]{new CreateHtmlDescriptionFix(aClass.getName(), module, true)}, + ProblemHighlightType.GENERIC_ERROR_OR_WARNING); + return new ProblemDescriptor[]{problemDescriptor}; + } + + public static List getPotentialRoots(Module module) { + final PsiDirectory[] dirs = getIntentionDescriptionsDirs(module); + final List result = new ArrayList(); + if (dirs.length != 0) { + for (PsiDirectory dir : dirs) { + final PsiDirectory parent = dir.getParentDirectory(); + if (parent != null) result.add(parent.getVirtualFile()); + } + } else { + result.addAll(Arrays.asList(ModuleRootManager.getInstance(module).getSourceRoots())); + } + return result; + } + + public static PsiDirectory[] getIntentionDescriptionsDirs(Module module) { + final PsiPackage aPackage = JavaPsiFacade.getInstance(module.getProject()).findPackage(INSPECTION_DESCRIPTIONS); + if (aPackage != null) { + return aPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module)); + } else { + return PsiDirectory.EMPTY_ARRAY; + } + } + + @Nullable + private static PsiMethod findNearestMethod(String name, @Nullable PsiClass cls) { + if (cls == null) return null; + for (PsiMethod method : cls.getMethods()) { + if (method.getParameterList().getParametersCount() == 0 && method.getName().equals(name)) { + return method.getModifierList().hasModifierProperty(PsiModifier.ABSTRACT) ? null : method; + } + } + return findNearestMethod(name, cls.getSuperClass()); + } + + @Nls + @NotNull + public String getDisplayName() { + return "Intention Description Checker"; + } + + @NotNull + public String getShortName() { + return "IntentionDescriptionNotFoundInspection"; + } + + @Override + public boolean isEnabledByDefault() { + return true; + } +} \ No newline at end of file diff --git a/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java b/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java index b1c3986e5d54..1c1b708cd330 100644 --- a/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java +++ b/plugins/devkit/src/inspections/quickfix/CreateHtmlDescriptionFix.java @@ -42,7 +42,8 @@ import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.devkit.DevKitBundle; -import org.jetbrains.idea.devkit.inspections.DescriptionNotFoundInspection; +import org.jetbrains.idea.devkit.inspections.InspectionDescriptionNotFoundInspection; +import org.jetbrains.idea.devkit.inspections.IntentionDescriptionNotFoundInspection; import javax.swing.*; import java.io.File; @@ -54,14 +55,15 @@ import java.util.List; */ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable { private final String myFilename; - private final Module myModule; - @NonNls private static final String DESCRIPTIONS_FOLDER = "inspectionDescriptions"; + private final Module myModule; @NonNls private static final String TEMPLATE_NAME = "InspectionDescription.html"; private static final Icon NEW_HTML_ICON = IconLoader.getIcon("/new_html.png"); + private final boolean isIntention; - public CreateHtmlDescriptionFix(String filename, Module module) { + public CreateHtmlDescriptionFix(String filename, Module module, boolean isIntention) { myModule = module; - myFilename = filename + ".html"; + this.isIntention = isIntention; + myFilename = isIntention ? filename : filename + ".html"; } @NotNull @@ -75,7 +77,10 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable { } public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final List virtualFiles = DescriptionNotFoundInspection.getPotentialRoots(myModule); + final List virtualFiles = isIntention ? + IntentionDescriptionNotFoundInspection.getPotentialRoots(myModule) + : + InspectionDescriptionNotFoundInspection.getPotentialRoots(myModule); final VirtualFile[] roots = prepare(VfsUtil.toVirtualFileArray(virtualFiles)); if (roots.length == 1) { ApplicationManager.getApplication().runWriteAction(new Runnable() { @@ -88,7 +93,11 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable { else { List options = new ArrayList(); for (VirtualFile file : roots) { - options.add(file.getPresentableUrl() + File.separator + DESCRIPTIONS_FOLDER + File.separator + myFilename); + String path = file.getPresentableUrl() + File.separator + getDescriptionFolderName() + File.separator + myFilename; + if (isIntention) { + path += File.separator + "description.html"; + } + options.add(path); } final JList files = new JBList(ArrayUtil.toStringArray(options)); final PopupChooserBuilder builder = JBPopupFactory.getInstance().createListPopupBuilder(files); @@ -117,16 +126,22 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable { PsiDirectory descrRoot = null; if (psiRoot == null) return; for (PsiDirectory dir : psiRoot.getSubdirectories()) { - if (DESCRIPTIONS_FOLDER.equals(dir.getName())) { + if (getDescriptionFolderName().equals(dir.getName())) { descrRoot = dir; break; } } try { - descrRoot = descrRoot == null ? psiRoot.createSubdirectory(DESCRIPTIONS_FOLDER) : descrRoot; + descrRoot = descrRoot == null ? psiRoot.createSubdirectory(getDescriptionFolderName()) : descrRoot; + if (isIntention) { + PsiDirectory dir = descrRoot.findSubdirectory(myFilename); + if (dir == null) { + descrRoot = descrRoot.createSubdirectory(myFilename); + } + } final FileTemplate descrTemplate = FileTemplateManager.getInstance().getJ2eeTemplate(TEMPLATE_NAME); - final PsiElement template = FileTemplateUtil.createFromTemplate(descrTemplate, myFilename, null, descrRoot); + final PsiElement template = FileTemplateUtil.createFromTemplate(descrTemplate, isIntention? "description.html" : myFilename, null, descrRoot); if (template instanceof PsiFile) { final VirtualFile file = ((PsiFile)template).getVirtualFile(); if (file != null) { @@ -142,7 +157,7 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable { return NEW_HTML_ICON; } - private static VirtualFile[] prepare(VirtualFile[] roots) { + private VirtualFile[] prepare(VirtualFile[] roots) { List found = new ArrayList(); for (VirtualFile root : roots) { if (containsDescriptionDir(root)) { @@ -152,13 +167,17 @@ public class CreateHtmlDescriptionFix implements LocalQuickFix, Iconable { return found.size() > 0 ? VfsUtil.toVirtualFileArray(found) : roots; } - private static boolean containsDescriptionDir(VirtualFile root) { + private boolean containsDescriptionDir(VirtualFile root) { if (!root.isDirectory()) return false; for (VirtualFile file : root.getChildren()) { - if (file.isDirectory() && DESCRIPTIONS_FOLDER.equals(file.getName())) { + if (file.isDirectory() && getDescriptionFolderName().equals(file.getName())) { return true; } } return false; } + + private String getDescriptionFolderName() { + return isIntention ? "intentionDescriptions" : "inspectionDescriptions"; + } }