diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXHighlightingTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXHighlightingTest.java index 4cc51f704f16..4da1ad73daae 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXHighlightingTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXHighlightingTest.java @@ -501,7 +501,11 @@ public class JavaFXHighlightingTest extends AbstractJavaFXTestCase { doTest(); } - private void doTest() throws Exception { + public void testConstructorNamedArg() { + doTest(); + } + + private void doTest() { myFixture.testHighlighting(false, false, false, getTestName(true) + ".fxml"); } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java index 25cec6a5dbfd..99b202da5ded 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java @@ -903,8 +903,8 @@ public class JavaFxPsiUtil { if (!constructor.hasModifierProperty(PsiModifier.PUBLIC)) continue; final PsiParameter[] parameters = constructor.getParameterList().getParameters(); for (PsiParameter parameter : parameters) { - String propertyName = getPropertyNameFromNamedArgAnnotation(parameter); - if (propertyName != null && !acceptableMembers.containsKey(propertyName)) { + final String propertyName = getPropertyNameFromNamedArgAnnotation(parameter); + if (!StringUtil.isEmpty(propertyName) && !acceptableMembers.containsKey(propertyName)) { final PsiField field = psiClass.findFieldByName(propertyName, true); if (field != null && !field.hasModifierProperty(PsiModifier.STATIC)) { acceptableMembers.put(propertyName, field); @@ -966,6 +966,34 @@ public class JavaFxPsiUtil { return null; } + /** + * Unlike normal properties (fields, getters/setters) named constructor parameters can be declared many times, possibly with different types + */ + @NotNull + public static Set getConstructorNamedArgProperties(@Nullable PsiClass psiClass) { + if (psiClass != null) { + return CachedValuesManager.getCachedValue(psiClass, () -> CachedValueProvider.Result.create( + prepareConstructorNamedArgProperties(psiClass), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT)); + } + return Collections.emptySet(); + } + + private static Set prepareConstructorNamedArgProperties(@NotNull PsiClass psiClass) { + final Set properties = new THashSet<>(); + for (PsiMethod constructor : psiClass.getConstructors()) { + if (constructor.hasModifierProperty(PsiModifier.PUBLIC)) { + final PsiParameter[] parameters = constructor.getParameterList().getParameters(); + for (PsiParameter parameter : parameters) { + final String propertyName = getPropertyNameFromNamedArgAnnotation(parameter); + if (!StringUtil.isEmpty(propertyName)) { + properties.add(propertyName); + } + } + } + } + return properties; + } + @Nullable public static PsiMethod findInstancePropertySetter(@NotNull PsiClass psiClass, @Nullable String propertyName) { if (StringUtil.isEmpty(propertyName)) return null; diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxClassTagDescriptorBase.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxClassTagDescriptorBase.java index db3a131dbe22..84d2bd4fbfab 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxClassTagDescriptorBase.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/descriptors/JavaFxClassTagDescriptorBase.java @@ -24,10 +24,7 @@ import org.jetbrains.plugins.javaFX.fxml.FxmlConstants; import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; /** * User: anna @@ -60,7 +57,7 @@ public abstract class JavaFxClassTagDescriptorBase implements XmlElementDescript if (psiClass != null) { final List children = new ArrayList<>(); collectWritableProperties(children, - (member) -> new JavaFxPropertyTagDescriptor(psiClass, PropertyUtil.getPropertyName(member), false)); + member -> new JavaFxPropertyTagDescriptor(psiClass, PropertyUtil.getPropertyName(member), false)); final JavaFxPropertyTagDescriptor defaultPropertyDescriptor = getDefaultPropertyDescriptor(); if (defaultPropertyDescriptor != null) { @@ -217,15 +214,24 @@ public abstract class JavaFxClassTagDescriptorBase implements XmlElementDescript } protected void collectInstanceProperties(List simpleAttrs) { - collectWritableProperties(simpleAttrs, - (member) -> new JavaFxPropertyAttributeDescriptor(PropertyUtil.getPropertyName(member), getPsiClass())); + final PsiClass psiClass = getPsiClass(); + final Set propertyNames = collectWritableProperties( + simpleAttrs, member -> new JavaFxPropertyAttributeDescriptor(PropertyUtil.getPropertyName(member), psiClass)); + + for (String name : JavaFxPsiUtil.getConstructorNamedArgProperties(psiClass)) { + if (!propertyNames.contains(name)) { + simpleAttrs.add(new JavaFxPropertyAttributeDescriptor(name, psiClass)); + } + } } - private void collectWritableProperties(final List children, final Function factory) { + @NotNull + private Set collectWritableProperties(final List children, final Function factory) { final Map fieldList = JavaFxPsiUtil.collectWritableProperties(getPsiClass()); for (PsiMember field : fieldList.values()) { children.add(factory.fun(field)); } + return fieldList.keySet(); } @Nullable @@ -244,6 +250,9 @@ public abstract class JavaFxClassTagDescriptorBase implements XmlElementDescript if (psiMember != null) { return new JavaFxPropertyAttributeDescriptor(attributeName, psiClass); } + if (JavaFxPsiUtil.getConstructorNamedArgProperties(psiClass).contains(attributeName)) { + return new JavaFxPropertyAttributeDescriptor(attributeName, psiClass); + } return null; } @@ -321,7 +330,9 @@ public abstract class JavaFxClassTagDescriptorBase implements XmlElementDescript public boolean isReadOnlyAttribute(String attributeName) { final PsiClass psiClass = getPsiClass(); - return psiClass != null && !JavaFxPsiUtil.collectWritableProperties(psiClass).containsKey(attributeName); + return psiClass != null && + !JavaFxPsiUtil.collectWritableProperties(psiClass).containsKey(attributeName) && + !JavaFxPsiUtil.getConstructorNamedArgProperties(psiClass).contains(attributeName); } @NotNull diff --git a/plugins/javaFX/testData/highlighting/constructorNamedArg.fxml b/plugins/javaFX/testData/highlighting/constructorNamedArg.fxml new file mode 100644 index 000000000000..f96eaa31567d --- /dev/null +++ b/plugins/javaFX/testData/highlighting/constructorNamedArg.fxml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file