From c3a34f9372965134c8a6d5bad1c63137480b0d5e Mon Sep 17 00:00:00 2001 From: Pavel Dolgov Date: Thu, 25 Feb 2016 16:48:09 +0300 Subject: [PATCH] javafx: Use modern API for sorting completion suggestions, store completion suggestions in cache (IDEA-150446) --- .../javaFX/fxml/JavaFxCompletionTest.java | 21 ++++++- .../plugins/javaFX/fxml/JavaFxPsiUtil.java | 57 ++++++++++++------- .../JavaFxComponentIdReferenceProvider.java | 13 ++--- .../variableCompletionBooleanFirst.fxml | 25 ++++++++ .../variableCompletionTooltipFirst.fxml | 20 +++++++ 5 files changed, 106 insertions(+), 30 deletions(-) create mode 100644 plugins/javaFX/testData/completion/variableCompletionBooleanFirst.fxml create mode 100644 plugins/javaFX/testData/completion/variableCompletionTooltipFirst.fxml diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxCompletionTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxCompletionTest.java index a701a98eff54..426541d8c120 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxCompletionTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFxCompletionTest.java @@ -22,7 +22,10 @@ import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; import java.util.stream.Collectors; /** @@ -240,6 +243,20 @@ public class JavaFxCompletionTest extends LightFixtureCompletionTestCase { doOptionsTest(Arrays.asList("pane", "node", "box", "model", "text", "target"), "FxIdGuessedOptionsController"); } + public void testVariableCompletionBooleanFirst() throws Exception { + doOrderTest("zAssignable", "dConvertible", "tConvertible", "controller", "mUnknown"); + } + + public void testVariableCompletionTooltipFirst() throws Exception { + doOrderTest("tAssignable", "controller", "mUnknown", "dIncompatible"); + } + + private void doOrderTest(String... expected) { + myFixture.configureByFiles(getTestName(true) + ".fxml"); + complete(); + assertOrderedEquals(myFixture.getLookupElementStrings(), expected); + } + private void doOptionsTest(final List expectedOptions, final String... javaClasses) { final List files = new ArrayList<>(); files.add(getTestName(true) + ".fxml"); @@ -248,7 +265,7 @@ public class JavaFxCompletionTest extends LightFixtureCompletionTestCase { complete(); final Set actualOptions = Arrays.stream(myItems).map(LookupElement::getLookupString).collect(Collectors.toSet()); - assertEquals(new HashSet<>(expectedOptions), actualOptions); + assertSameElements(expectedOptions, actualOptions); } public void testOnlyCssAsStylesheets() throws Exception { 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 7a6232bf42c3..a4a44b84b2d5 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/JavaFxPsiUtil.java @@ -35,6 +35,7 @@ import com.intellij.psi.xml.*; import com.intellij.util.Processor; import com.intellij.xml.XmlAttributeDescriptor; import com.intellij.xml.XmlElementDescriptor; +import gnu.trove.THashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxClassBackedElementDescriptor; @@ -603,31 +604,45 @@ public class JavaFxPsiUtil { return null; } + private static final Key>> FILE_IDS_KEY = Key.create("FILE_IDS_KEY"); + @NotNull - public static Map collectFileIds(final XmlTag currentTag) { - final Map fileIds = new HashMap(); - currentTag.getContainingFile().accept(new XmlRecursiveElementVisitor() { - @Override - public void visitXmlTag(XmlTag tag) { - super.visitXmlTag(tag); - if (currentTag != tag) { - final XmlAttribute attribute = tag.getAttribute(FxmlConstants.FX_ID); - if (attribute != null) { - fileIds.put(attribute.getValue(), attribute.getValueElement()); - } - } - } - }); + public static Map collectFileIds(@Nullable final XmlTag currentTag) { + if (currentTag == null) return Collections.emptyMap(); final PsiFile containingFile = currentTag.getContainingFile(); - if (containingFile instanceof XmlFile) { - final XmlTag rootTag = ((XmlFile)containingFile).getRootTag(); - if (rootTag != null) { - final XmlAttribute attribute = rootTag.getAttribute(FxmlConstants.FX_CONTROLLER); - if (attribute != null) { - fileIds.put(FxmlConstants.CONTROLLER, attribute.getValueElement()); - } + if (!(containingFile instanceof XmlFile)) return Collections.emptyMap(); + final XmlTag rootTag = ((XmlFile)containingFile).getRootTag(); + if (rootTag == null) return Collections.emptyMap(); + + final Map cachedIds = CachedValuesManager.getManager(containingFile.getProject()) + .getCachedValue(rootTag, FILE_IDS_KEY, + () -> new CachedValueProvider.Result<>(prepareFileIds(rootTag), PsiModificationTracker.MODIFICATION_COUNT), false); + + final XmlAttribute currentIdAttribute = currentTag.getAttribute(FxmlConstants.FX_ID); + if (currentIdAttribute != null) { + final String currentId = currentIdAttribute.getValue(); + if (cachedIds.containsKey(currentId)) { + final Map filteredIds = new THashMap<>(cachedIds); + filteredIds.remove(currentId); + return filteredIds; } } + return cachedIds; + } + + @NotNull + private static Map prepareFileIds(XmlTag rootTag) { + final Map fileIds = new THashMap<>(); + for (XmlTag tag : SyntaxTraverser.psiTraverser().withRoot(rootTag).filter(XmlTag.class)) { + final XmlAttribute idAttribute = tag.getAttribute(FxmlConstants.FX_ID); + if (idAttribute != null) { + fileIds.put(idAttribute.getValue(), idAttribute.getValueElement()); + } + } + final XmlAttribute controllerAttribute = rootTag.getAttribute(FxmlConstants.FX_CONTROLLER); + if (controllerAttribute != null) { + fileIds.put(FxmlConstants.CONTROLLER, controllerAttribute.getValueElement()); + } return fileIds; } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxComponentIdReferenceProvider.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxComponentIdReferenceProvider.java index e38a6210acb2..37e4764fe137 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxComponentIdReferenceProvider.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/fxml/refs/JavaFxComponentIdReferenceProvider.java @@ -15,7 +15,9 @@ */ package org.jetbrains.plugins.javaFX.fxml.refs; -import com.intellij.codeInsight.lookup.LookupItem; +import com.intellij.codeInsight.completion.PrioritizedLookupElement; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; @@ -185,12 +187,9 @@ class JavaFxComponentIdReferenceProvider extends PsiReferenceProvider { @NotNull @Override public Object[] getVariants() { - return myAcceptableIds.stream().map( - id -> { - LookupItem item = new LookupItem(id, id); - item.setPriority(TypeMatch.getPriority(myTypeMatches.get(id))); - return item; - }).toArray(LookupItem[]::new); + return myAcceptableIds.stream() + .map(id -> PrioritizedLookupElement.withPriority(LookupElementBuilder.create(id), TypeMatch.getPriority(myTypeMatches.get(id)))) + .toArray(LookupElement[]::new); } } diff --git a/plugins/javaFX/testData/completion/variableCompletionBooleanFirst.fxml b/plugins/javaFX/testData/completion/variableCompletionBooleanFirst.fxml new file mode 100644 index 000000000000..ab2adce254b0 --- /dev/null +++ b/plugins/javaFX/testData/completion/variableCompletionBooleanFirst.fxml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + +