From 4013479fa75e3a6e757aff37884771348f5131ab Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 30 Jun 2010 16:37:51 +0400 Subject: [PATCH 1/3] make PsiElementBasedIntentionAction good for PsiElement, don't make a developer to calculate PsiElement himself --- .../PsiElementBaseIntentionAction.java | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java b/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java index 851bbd62d281..45b68cc920c9 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java +++ b/platform/lang-api/src/com/intellij/codeInsight/intention/PsiElementBaseIntentionAction.java @@ -14,12 +14,6 @@ * limitations under the License. */ -/* - * Created by IntelliJ IDEA. - * User: Anna.Kozlova - * Date: 05-Nov-2006 - * Time: 18:04:58 - */ package com.intellij.codeInsight.intention; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; @@ -28,9 +22,45 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +/** + * @author Anna Kozlova + * @author Konstantin Bulenkov + */ public abstract class PsiElementBaseIntentionAction extends BaseIntentionAction { + /** + * Invokes intention action for the element under cursor + * + * @param project the project in which the file is opened. + * @param editor the editor for the file + * @param element the element under cursor + + * @throws com.intellij.util.IncorrectOperationException ... + */ + public void invoke(Project project, Editor editor, PsiElement element) throws IncorrectOperationException { + throw new IncorrectOperationException(); + } + + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + final PsiElement element = getElement(editor, file); + return element == null ? false : isAvailable(project, editor, element); + } + + @Nullable + protected static PsiElement getElement(Editor editor, PsiFile file) { + if (!file.getManager().isInProject(file)) return null; + final CaretModel caretModel = editor.getCaretModel(); + final int position = caretModel.getOffset(); + return file.findElementAt(position); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + invoke(project, editor, getElement(editor, file)); + } /** * Checks whether this intention is available at a caret offset in file. @@ -42,13 +72,4 @@ public abstract class PsiElementBaseIntentionAction extends BaseIntentionAction * @return true if the intention is available, false otherwise. */ public abstract boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element); - - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - if (!file.getManager().isInProject(file)) return false; - final CaretModel caretModel = editor.getCaretModel(); - final int position = caretModel.getOffset(); - final PsiElement element = file.findElementAt(position); - if (element == null) return false; - return isAvailable(project, editor, element); - } } \ No newline at end of file From aabb42b88ea342610465affb70ffd21ac207b720 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Wed, 30 Jun 2010 16:39:41 +0400 Subject: [PATCH 2/3] intention description check --- ...pectionDescriptionNotFoundInspection.html} | 2 +- .../src/DevKitInspectionToolProvider.java | 12 +- ...pectionDescriptionNotFoundInspection.java} | 6 +- ...ntentionDescriptionNotFoundInspection.java | 130 ++++++++++++++++++ .../quickfix/CreateHtmlDescriptionFix.java | 45 ++++-- 5 files changed, 174 insertions(+), 21 deletions(-) rename plugins/devkit/resources/inspectionDescriptions/{DescriptionNotFoundInspection.html => InspectionDescriptionNotFoundInspection.html} (94%) rename plugins/devkit/src/inspections/{DescriptionNotFoundInspection.java => InspectionDescriptionNotFoundInspection.java} (97%) create mode 100644 plugins/devkit/src/inspections/IntentionDescriptionNotFoundInspection.java 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"; + } } From 8d4348c815d9e0fa1074b31cb2263fd790738e73 Mon Sep 17 00:00:00 2001 From: Dmitry Trofimov Date: Wed, 30 Jun 2010 18:16:09 +0400 Subject: [PATCH 3/3] - regexp colors page --- RegExpSupport/src/META-INF/RegExpPlugin.xml | 1 + .../lang/regexp/RegExpColorsPage.java | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 RegExpSupport/src/org/intellij/lang/regexp/RegExpColorsPage.java diff --git a/RegExpSupport/src/META-INF/RegExpPlugin.xml b/RegExpSupport/src/META-INF/RegExpPlugin.xml index 2f1803fad272..54f6f622997a 100644 --- a/RegExpSupport/src/META-INF/RegExpPlugin.xml +++ b/RegExpSupport/src/META-INF/RegExpPlugin.xml @@ -8,5 +8,6 @@ + diff --git a/RegExpSupport/src/org/intellij/lang/regexp/RegExpColorsPage.java b/RegExpSupport/src/org/intellij/lang/regexp/RegExpColorsPage.java new file mode 100644 index 000000000000..8afcb738c59e --- /dev/null +++ b/RegExpSupport/src/org/intellij/lang/regexp/RegExpColorsPage.java @@ -0,0 +1,81 @@ +/* + * 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.intellij.lang.regexp; + +import com.intellij.application.options.colors.InspectionColorSettingsPage; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.openapi.fileTypes.SyntaxHighlighter; +import com.intellij.openapi.options.colors.AttributesDescriptor; +import com.intellij.openapi.options.colors.ColorDescriptor; +import com.intellij.openapi.options.colors.ColorSettingsPage; +import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.util.Map; + +/** + * @author traff + */ +public class RegExpColorsPage implements ColorSettingsPage, InspectionColorSettingsPage { + private static final AttributesDescriptor[] ATTRS = new AttributesDescriptor[] { + new AttributesDescriptor("Keywords", RegExpHighlighter.META), + new AttributesDescriptor("Escaped characters", RegExpHighlighter.ESC_CHARACTER), + new AttributesDescriptor("Braces", RegExpHighlighter.BRACES), + new AttributesDescriptor("Brackets", RegExpHighlighter.BRACKETS), + new AttributesDescriptor("Parenthesis", RegExpHighlighter.PARENTHS), + }; + + @NonNls private static final HashMap ourTagToDescriptorMap = new HashMap(); + + @NotNull + public String getDisplayName() { + return "RegExp"; + } + + public Icon getIcon() { + return RegExpFileType.INSTANCE.getIcon(); + } + + @NotNull + public AttributesDescriptor[] getAttributeDescriptors() { + return ATTRS; + } + + @NotNull + public ColorDescriptor[] getColorDescriptors() { + return ColorDescriptor.EMPTY_ARRAY; + } + + @NotNull + public SyntaxHighlighter getHighlighter() { + final SyntaxHighlighter highlighter = SyntaxHighlighter.PROVIDER.create(RegExpFileType.INSTANCE, null, null); + assert highlighter != null; + return highlighter; + } + + @NotNull + public String getDemoText() { + return + "^[\\w\\.-]+@([\\w\\-]+\\.)+[A-Z]{2,4}$"; + + } + + public Map getAdditionalHighlightingTagToDescriptorMap() { + return ourTagToDescriptorMap; + } +}