javafx: Use modern API for sorting completion suggestions, store completion suggestions in cache (IDEA-150446)

This commit is contained in:
Pavel Dolgov
2016-02-25 16:48:09 +03:00
parent 9f812e02db
commit c3a34f9372
5 changed files with 106 additions and 30 deletions
@@ -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<String> expectedOptions, final String... javaClasses) {
final List<String> files = new ArrayList<>();
files.add(getTestName(true) + ".fxml");
@@ -248,7 +265,7 @@ public class JavaFxCompletionTest extends LightFixtureCompletionTestCase {
complete();
final Set<String> actualOptions = Arrays.stream(myItems).map(LookupElement::getLookupString).collect(Collectors.toSet());
assertEquals(new HashSet<>(expectedOptions), actualOptions);
assertSameElements(expectedOptions, actualOptions);
}
public void testOnlyCssAsStylesheets() throws Exception {
@@ -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<CachedValue<Map<String, XmlAttributeValue>>> FILE_IDS_KEY = Key.create("FILE_IDS_KEY");
@NotNull
public static Map<String, XmlAttributeValue> collectFileIds(final XmlTag currentTag) {
final Map<String, XmlAttributeValue> fileIds = new HashMap<String, XmlAttributeValue>();
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<String, XmlAttributeValue> 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<String, XmlAttributeValue> 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<String, XmlAttributeValue> filteredIds = new THashMap<>(cachedIds);
filteredIds.remove(currentId);
return filteredIds;
}
}
return cachedIds;
}
@NotNull
private static Map<String, XmlAttributeValue> prepareFileIds(XmlTag rootTag) {
final Map<String, XmlAttributeValue> 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;
}
@@ -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<String> item = new LookupItem<String>(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);
}
}
@@ -0,0 +1,25 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tooltip?>
<?import java.lang.Double?>
<?import java.lang.Boolean?>
<?import sample.UnknownModel?>
<GridPane fx:controller="sample.Controller" xmlns:fx="http://javafx.com/fxml">
<fx:define>
<Tooltip text="tooltip" fx:id="tConvertible"/>
</fx:define>
<fx:define>
<Double fx:value="123.45" fx:id="dConvertible"/>
</fx:define>
<fx:define>
<Boolean fx:value="123.45" fx:id="zAssignable"/>
</fx:define>
<fx:define>
<UnknownModel fx:id="mUnknown"/>
</fx:define>
<Button onAction="#sayHello" text="Hello" fx:id="b1" defaultButton="$<caret>" />
</GridPane>
@@ -0,0 +1,20 @@
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tooltip?>
<?import java.lang.Double?>
<?import sample.UnknownModel?>
<GridPane fx:controller="sample.Controller" xmlns:fx="http://javafx.com/fxml">
<fx:define>
<Tooltip text="tooltip" fx:id="tAssignable"/>
</fx:define>
<fx:define>
<Double fx:value="123.45" fx:id="dIncompatible"/>
</fx:define>
<fx:define>
<UnknownModel fx:id="mUnknown"/>
</fx:define>
<Button onAction="#sayHello" text="Hello" fx:id="b1" tooltip="$<caret>" />
</GridPane>