javafx: navigation from controller fields to corresponding components (IDEA-100145)

This commit is contained in:
Anna Kozlova
2013-02-01 20:56:45 +04:00
parent deaf2c404d
commit 1eb6936907
5 changed files with 118 additions and 5 deletions
+1
View File
@@ -18,5 +18,6 @@
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxTagNameReference$JavaFxUnresolvedTagRefsProvider"/>
<multiHostInjector implementation="org.jetbrains.plugins.javaFX.fxml.ScriptLanguageInjector"/>
<annotator language="XML" implementationClass="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxAnnotator"/>
<codeInsight.lineMarkerProvider language="JAVA" implementationClass="org.jetbrains.plugins.javaFX.fxml.codeInsight.JavaFxRelatedItemLineMarkerProvider"/>
</extensions>
</idea-plugin>
@@ -15,6 +15,7 @@
*/
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.lang.ASTNode;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.project.Project;
@@ -246,4 +247,9 @@ public class JavaFxPsiUtil {
}
return null;
}
public static boolean isVisibleInFxml(PsiMember psiMember) {
return psiMember.hasModifierProperty(PsiModifier.PUBLIC) ||
AnnotationUtil.isAnnotated(psiMember, JavaFxCommonClassNames.JAVAFX_FXML_ANNOTATION, false);
}
}
@@ -0,0 +1,108 @@
/*
* 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.codeInsight;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.daemon.GutterIconNavigationHandler;
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.icons.AllIcons;
import com.intellij.navigation.GotoRelatedItem;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.util.CommonProcessors;
import com.intellij.util.ConstantFunction;
import com.intellij.util.Function;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory;
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collection;
/**
* User: anna
*/
public class JavaFxRelatedItemLineMarkerProvider extends RelatedItemLineMarkerProvider {
@Override
protected void collectNavigationMarkers(@NotNull PsiElement element, final Collection<? super RelatedItemLineMarkerInfo> result) {
if (element instanceof PsiField) {
final PsiField field = (PsiField)element;
if (JavaFxPsiUtil.isVisibleInFxml(field) && !field.hasModifierProperty(PsiModifier.STATIC)) {
final PsiClass containingClass = field.getContainingClass();
if (containingClass != null && containingClass.hasModifierProperty(PsiModifier.PUBLIC)) {
final ArrayList<GotoRelatedItem> targets = new ArrayList<GotoRelatedItem>();
collectTargets(element, targets, new Function<PsiElement, GotoRelatedItem>() {
@Override
public GotoRelatedItem fun(PsiElement element) {
return new GotoRelatedItem(element);
}
}, true);
if (targets.isEmpty()) return;
result.add(new RelatedItemLineMarkerInfo<PsiField>(field, field.getNameIdentifier().getTextRange(),
AllIcons.FileTypes.Xml, Pass.UPDATE_OVERRIDEN_MARKERS, null,
new JavaFXIdIconNavigationHandler(), GutterIconRenderer.Alignment.LEFT,
targets));
}
}
}
}
private static <T> void collectTargets(PsiElement element, final ArrayList<T> targets, final Function<PsiElement, T> fun, final boolean stopAtFirst) {
ReferencesSearch.search(element).forEach(new Processor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
final PsiElement referenceElement = reference.getElement();
if (referenceElement == null) return true;
if (!(referenceElement instanceof XmlAttributeValue)) return true;
final XmlAttributeValue attributeValue = (XmlAttributeValue)referenceElement;
final PsiElement parent = attributeValue.getParent();
if (!(parent instanceof XmlAttribute)) return true;
final PsiFile containingFile = referenceElement.getContainingFile();
if (containingFile == null) return true;
if (JavaFxFileTypeFactory.isFxml(containingFile)) {
targets.add(fun.fun(parent));
if (stopAtFirst) return false;
}
return true;
}
});
}
private static class JavaFXIdIconNavigationHandler implements GutterIconNavigationHandler<PsiField> {
@Override
public void navigate(MouseEvent e, PsiField field) {
final ArrayList<PsiElement> relatedItems = new ArrayList<PsiElement>();
collectTargets(field, relatedItems, Function.ID, false);
if (relatedItems.size() == 1) {
NavigationUtil.activateFileWithPsiElement(relatedItems.get(0));
return;
}
final JBPopup popup = NavigationUtil
.getPsiElementPopup(relatedItems.toArray(new PsiElement[relatedItems.size()]), "<html>Choose component with fx:id <b>" + field.getName() + "<b></html>");
popup.show(new RelativePoint(e));
}
}
}
@@ -59,8 +59,7 @@ public class JavaFxAnnotator implements Annotator {
for (PsiReference reference : references) {
final PsiElement resolve = reference.resolve();
if (resolve instanceof PsiMember) {
if (!((PsiMember)resolve).hasModifierProperty(PsiModifier.PUBLIC) &&
!AnnotationUtil.isAnnotated((PsiMember)resolve, Collections.singleton(JavaFxCommonClassNames.JAVAFX_FXML_ANNOTATION))) {
if (!JavaFxPsiUtil.isVisibleInFxml((PsiMember)resolve)) {
final String symbolPresentation = "'" + SymbolPresentationUtil.getSymbolPresentableText(resolve) + "'";
final Annotation annotation = holder.createErrorAnnotation(element,
symbolPresentation + (resolve instanceof PsiClass ? " should be public" : " should be public or annotated with @FXML"));
@@ -15,14 +15,13 @@
*/
package org.jetbrains.plugins.javaFX.fxml.refs;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.psi.*;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonClassNames;
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil;
import java.util.ArrayList;
import java.util.List;
@@ -51,7 +50,7 @@ class JavaFxFieldIdReferenceProvider extends JavaFxControllerBasedReferenceProvi
final PsiField[] fields = aClass.getFields();
for (PsiField psiField : fields) {
if (!psiField.hasModifierProperty(PsiModifier.STATIC)) {
if (psiField.hasModifierProperty(PsiModifier.PUBLIC) || AnnotationUtil.isAnnotated(psiField, JavaFxCommonClassNames.JAVAFX_FXML_ANNOTATION, false)) {
if (JavaFxPsiUtil.isVisibleInFxml(psiField)) {
fieldsToSuggest.add(psiField);
}
}