From 14675bb8eb39bfc23167e6a2a058172e56d1dd3f Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Fri, 25 Jan 2013 12:28:00 +0400 Subject: [PATCH] javafx: add single imports for unresolved tags --- .../daemon/impl/quickfix/ImportClassFix.java | 4 +- .../impl/quickfix/ImportClassFixBase.java | 58 +++++++------ .../intentions/GroovyAddImportAction.java | 4 +- plugins/javaFX/src/META-INF/plugin.xml | 1 + .../javaFX/fxml/JavaFxImportClassFix.java | 82 +++++++++++++++++++ .../javaFX/fxml/JavaFxTagNameReference.java | 72 ++++++++++++---- .../testData/importing/simpleImport.fxml | 8 ++ .../importing/simpleImport_after.fxml | 9 ++ .../plugins/javaFX/fxml/JavaFXImportTest.java | 51 ++++++++++++ 9 files changed, 245 insertions(+), 44 deletions(-) create mode 100644 plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxImportClassFix.java create mode 100644 plugins/javaFX/testData/importing/simpleImport.fxml create mode 100644 plugins/javaFX/testData/importing/simpleImport_after.fxml create mode 100644 plugins/javaFX/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXImportTest.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFix.java index f9fcb60ec405..ca346d3a3742 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFix.java @@ -32,9 +32,9 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public class ImportClassFix extends ImportClassFixBase { +public class ImportClassFix extends ImportClassFixBase { public ImportClassFix(@NotNull PsiJavaCodeReferenceElement element) { - super(element); + super(element, element); } @Override diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java index 0a2f43c6d7fc..067c6bdb2fe0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java @@ -57,20 +57,22 @@ import java.util.regex.PatternSyntaxException; /** * @author peter */ -public abstract class ImportClassFixBase implements HintAction, HighPriorityAction { - private final T myRef; +public abstract class ImportClassFixBase implements HintAction, HighPriorityAction { + private final T myElement; + private final R myRef; - protected ImportClassFixBase(@NotNull T ref) { + protected ImportClassFixBase(@NotNull T elem, R ref) { + myElement = elem; myRef = ref; } @Override public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiFile file) { - if (!myRef.isValid()) { + if (!myElement.isValid()) { return false; } - PsiElement parent = myRef.getParent(); + PsiElement parent = myElement.getParent(); if (parent instanceof PsiNewExpression && ((PsiNewExpression)parent).getQualifier() != null) { return false; } @@ -80,15 +82,15 @@ public abstract class ImportClassFixBase im } @Nullable - protected abstract String getReferenceName(@NotNull T reference); - protected abstract PsiElement getReferenceNameElement(@NotNull T reference); - protected abstract boolean hasTypeParameters(@NotNull T reference); + protected abstract String getReferenceName(@NotNull R reference); + protected abstract PsiElement getReferenceNameElement(@NotNull R reference); + protected abstract boolean hasTypeParameters(@NotNull R reference); @NotNull public List getClassesToImport() { - PsiShortNamesCache cache = PsiShortNamesCache.getInstance(myRef.getProject()); + PsiShortNamesCache cache = PsiShortNamesCache.getInstance(myElement.getProject()); String name = getReferenceName(myRef); - GlobalSearchScope scope = myRef.getResolveScope(); + GlobalSearchScope scope = myElement.getResolveScope(); if (name == null) { return Collections.emptyList(); } @@ -96,7 +98,7 @@ public abstract class ImportClassFixBase im PsiClass[] classes = cache.getClassesByName(name, scope); if (classes.length == 0) return Collections.emptyList(); List classList = new ArrayList(classes.length); - boolean isAnnotationReference = myRef.getParent() instanceof PsiAnnotation; + boolean isAnnotationReference = myElement.getParent() instanceof PsiAnnotation; for (PsiClass aClass : classes) { if (isAnnotationReference && !aClass.isAnnotationType()) continue; if (JavaCompletionUtil.isInExcludedPackage(aClass, false)) continue; @@ -105,26 +107,26 @@ public abstract class ImportClassFixBase im if (qName != null) { //filter local classes if (qName.indexOf('.') == -1) continue; //do not show classes from default package) if (qName.endsWith(name)) { - if (isAccessible(aClass, myRef)) { + if (isAccessible(aClass, myElement)) { classList.add(aClass); } } } } - final String memberName = getRequiredMemberName(myRef); + final String memberName = getRequiredMemberName(myElement); if (memberName != null) { List filtered = ContainerUtil.findAll(classList, new Condition() { @Override public boolean value(PsiClass psiClass) { PsiField field = psiClass.findFieldByName(memberName, true); - if (field != null && field.hasModifierProperty(PsiModifier.STATIC) && isAccessible(field, myRef)) return true; + if (field != null && field.hasModifierProperty(PsiModifier.STATIC) && isAccessible(field, myElement)) return true; PsiClass inner = psiClass.findInnerClassByName(memberName, true); - if (inner != null && isAccessible(inner, myRef)) return true; + if (inner != null && isAccessible(inner, myElement)) return true; for (PsiMethod method : psiClass.findMethodsByName(memberName, true)) { - if (method.hasModifierProperty(PsiModifier.STATIC) && isAccessible(method, myRef)) return true; + if (method.hasModifierProperty(PsiModifier.STATIC) && isAccessible(method, myElement)) return true; } return false; } @@ -134,7 +136,7 @@ public abstract class ImportClassFixBase im } } - List filtered = filterByContext(classList, myRef); + List filtered = filterByContext(classList, myElement); if (!filtered.isEmpty()) { classList = filtered; } @@ -179,7 +181,7 @@ public abstract class ImportClassFixBase im if (classesToImport.isEmpty()) return Result.POPUP_NOT_SHOWN; try { - String name = getQualifiedName(myRef); + String name = getQualifiedName(myElement); if (name != null) { Pattern pattern = Pattern.compile(DaemonCodeAnalyzerSettings.getInstance().NO_AUTO_IMPORT_PATTERN); Matcher matcher = pattern.matcher(name); @@ -191,12 +193,12 @@ public abstract class ImportClassFixBase im catch (PatternSyntaxException e) { //ignore } - final PsiFile psiFile = myRef.getContainingFile(); + final PsiFile psiFile = myElement.getContainingFile(); if (classesToImport.size() > 1) { reduceSuggestedClassesBasedOnDependencyRuleViolation(psiFile, classesToImport); } PsiClass[] classes = classesToImport.toArray(new PsiClass[classesToImport.size()]); - final Project project = myRef.getProject(); + final Project project = myElement.getProject(); CodeInsightUtil.sortIdenticalShortNameClasses(classes, myRef); final QuestionAction action = createAddImportAction(classes, project, editor); @@ -226,14 +228,22 @@ public abstract class ImportClassFixBase im if (allowPopup && canImportHere) { String hintText = ShowAutoImportPass.getMessage(classes.length > 1, classes[0].getQualifiedName()); if (!ApplicationManager.getApplication().isUnitTestMode() && !HintManager.getInstance().hasShownHintsThatWillHideByOtherHint(true)) { - HintManager.getInstance().showQuestionHint(editor, hintText, myRef.getTextOffset(), - myRef.getTextRange().getEndOffset(), action); + HintManager.getInstance().showQuestionHint(editor, hintText, getStartOffset(myElement, myRef), + getEndOffset(myElement, myRef), action); } return Result.POPUP_SHOWN; } return Result.POPUP_NOT_SHOWN; } + protected int getStartOffset(T element, R ref) { + return element.getTextOffset(); + } + + protected int getEndOffset(T element, R ref) { + return element.getTextRange().getEndOffset(); + } + private static boolean autoImportWillInsertUnexpectedCharacters(PsiClass aClass) { PsiClass containingClass = aClass.getContainingClass(); // when importing inner class, the reference might be qualified with outer class name and it can be confusing @@ -245,7 +255,7 @@ public abstract class ImportClassFixBase im !hasUnresolvedImportWhichCanImport(psiFile, exampleClassName); } - protected abstract boolean isQualified(T reference); + protected abstract boolean isQualified(R reference); @Override public boolean showHint(final Editor editor) { @@ -290,7 +300,7 @@ public abstract class ImportClassFixBase im } } - private boolean isCaretNearRef(@NotNull Editor editor, @NotNull T ref) { + private boolean isCaretNearRef(@NotNull Editor editor, @NotNull R ref) { PsiElement nameElement = getReferenceNameElement(ref); if (nameElement == null) return false; TextRange range = nameElement.getTextRange(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyAddImportAction.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyAddImportAction.java index 7862d4a59866..c4cf6f26b256 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyAddImportAction.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/GroovyAddImportAction.java @@ -33,9 +33,9 @@ import java.util.List; /** * @author peter */ -public class GroovyAddImportAction extends ImportClassFixBase { +public class GroovyAddImportAction extends ImportClassFixBase { public GroovyAddImportAction(GrReferenceElement ref) { - super(ref); + super(ref, ref); } @Override diff --git a/plugins/javaFX/src/META-INF/plugin.xml b/plugins/javaFX/src/META-INF/plugin.xml index b15de6d5cab4..555dc02e27c9 100644 --- a/plugins/javaFX/src/META-INF/plugin.xml +++ b/plugins/javaFX/src/META-INF/plugin.xml @@ -12,5 +12,6 @@ + diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxImportClassFix.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxImportClassFix.java new file mode 100644 index 000000000000..e9f5cbda6eb6 --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxImportClassFix.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2013 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.plugins.javaFX.fxml; + +import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFixBase; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMember; +import com.intellij.psi.xml.XmlTag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** +* User: anna +*/ +abstract class JavaFxImportClassFix extends ImportClassFixBase { + + public JavaFxImportClassFix(JavaFxTagNameReference ref, XmlTag element) { + super(element, ref); + } + + protected abstract XmlTag getTagElement(JavaFxTagNameReference ref); + + @Nullable + @Override + protected String getReferenceName(@NotNull JavaFxTagNameReference reference) { + return getTagElement(reference).getName(); + } + + @Override + protected PsiElement getReferenceNameElement(@NotNull JavaFxTagNameReference reference) { + return getTagElement(reference).getNavigationElement(); + } + + @Override + protected boolean hasTypeParameters(@NotNull JavaFxTagNameReference reference) { + return false; + } + + @Override + protected boolean isAccessible(PsiMember member, XmlTag reference) { + return true; + } + + @Override + protected String getQualifiedName(XmlTag tag) { + return tag.getDescriptor().getQualifiedName(); + } + + @Override + protected boolean isQualified(JavaFxTagNameReference reference) { + return false; + } + + @Override + protected boolean hasUnresolvedImportWhichCanImport(PsiFile psiFile, String name) { + return false; //todo + } + + @Override + protected int getStartOffset(XmlTag element, JavaFxTagNameReference ref) { + return element.getTextOffset() + ref.getRangeInElement().getStartOffset(); + } + + @Override + protected int getEndOffset(XmlTag element, JavaFxTagNameReference ref) { + return element.getTextOffset() + ref.getRangeInElement().getEndOffset(); + } +} diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxTagNameReference.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxTagNameReference.java index e6c056b96209..7b7b32e83338 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxTagNameReference.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxTagNameReference.java @@ -2,14 +2,19 @@ package org.jetbrains.plugins.javaFX.fxml; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.completion.XmlTagInsertHandler; +import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.source.PostprocessReformattingAspect; import com.intellij.psi.impl.source.xml.TagNameReference; import com.intellij.psi.xml.*; import com.intellij.util.Function; +import com.intellij.util.IncorrectOperationException; import com.intellij.xml.XmlElementDescriptor; import org.jetbrains.annotations.NotNull; @@ -21,11 +26,23 @@ import java.util.List; * User: anna * Date: 1/8/13 */ -public class JavaFxTagNameReference extends TagNameReference { +public class JavaFxTagNameReference extends TagNameReference{ public JavaFxTagNameReference(ASTNode element, boolean startTagFlag) { super(element, startTagFlag); } + @Override + public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { + if (element instanceof PsiClass) { + final String qualifiedName = ((PsiClass)element).getQualifiedName(); + if (qualifiedName != null) { + insertImportWhenNeeded((XmlFile)getElement().getContainingFile(), StringUtil.getShortName(qualifiedName), qualifiedName); + return getElement(); + } + } + return super.bindToElement(element); + } + @NotNull @Override public LookupElement[] getVariants() { @@ -45,6 +62,42 @@ public class JavaFxTagNameReference extends TagNameReference { return elements.toArray(new LookupElement[elements.size()]); } + private static void insertImportWhenNeeded(XmlFile xmlFile, + String shortName, + String qualifiedName) { + if (shortName != null && JavaFxClassBackedElementDescriptor.findPsiClass(shortName, xmlFile.getRootTag()) == null) { + final XmlDocument document = xmlFile.getDocument(); + if (document != null) { + final XmlProcessingInstruction processingInstruction = JavaFxPsiUtil.createSingleImportInstruction(qualifiedName, xmlFile.getProject()); + final XmlProlog prolog = document.getProlog(); + if (prolog != null) { + prolog.add(processingInstruction); + } else { + document.addBefore(processingInstruction, document.getRootTag()); + } + PostprocessReformattingAspect.getInstance(xmlFile.getProject()).doPostponedFormatting(xmlFile.getViewProvider()); + } + } + } + + public static class JavaFxUnresolvedTagRefsProvider extends UnresolvedReferenceQuickFixProvider { + @Override + public void registerFixes(JavaFxTagNameReference ref, QuickFixActionRegistrar registrar) { + registrar.register(new JavaFxImportClassFix(ref, ref.getTagElement()) { + @Override + protected XmlTag getTagElement(JavaFxTagNameReference ref) { + return ref.getTagElement(); + } + }); + } + + @NotNull + @Override + public Class getReferenceClass() { + return JavaFxTagNameReference.class; + } + } + private static class JavaFxTagInsertHandler extends XmlTagInsertHandler { public static final JavaFxTagInsertHandler INSTANCE = new JavaFxTagInsertHandler(); @@ -55,21 +108,8 @@ public class JavaFxTagNameReference extends TagNameReference { if (object instanceof JavaFxClassBackedElementDescriptor) { final XmlFile xmlFile = (XmlFile)context.getFile(); final String shortName = ((JavaFxClassBackedElementDescriptor)object).getName(); - if (shortName != null && JavaFxClassBackedElementDescriptor.findPsiClass(shortName, xmlFile.getRootTag()) == null) { - final XmlProcessingInstruction processingInstruction = JavaFxPsiUtil - .createSingleImportInstruction(((JavaFxClassBackedElementDescriptor)object).getQualifiedName(), context.getProject()); - final XmlDocument document = xmlFile.getDocument(); - if (document != null) { - final XmlProlog prolog = document.getProlog(); - if (prolog != null) { - prolog.add(processingInstruction); - } else { - document.addBefore(processingInstruction, document.getRootTag()); - } - context.commitDocument(); - PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting(context.getFile().getViewProvider()); - } - } + insertImportWhenNeeded(xmlFile, shortName, ((JavaFxClassBackedElementDescriptor)object).getQualifiedName()); + context.commitDocument(); } } } diff --git a/plugins/javaFX/testData/importing/simpleImport.fxml b/plugins/javaFX/testData/importing/simpleImport.fxml new file mode 100644 index 000000000000..394432faf90c --- /dev/null +++ b/plugins/javaFX/testData/importing/simpleImport.fxml @@ -0,0 +1,8 @@ + + + + + + ton/> + + \ No newline at end of file diff --git a/plugins/javaFX/testData/importing/simpleImport_after.fxml b/plugins/javaFX/testData/importing/simpleImport_after.fxml new file mode 100644 index 000000000000..9c070608a8f3 --- /dev/null +++ b/plugins/javaFX/testData/importing/simpleImport_after.fxml @@ -0,0 +1,9 @@ + + + + + + +