javafx: completion of class tags: insert import if needed

This commit is contained in:
Anna Kozlova
2013-01-24 21:18:52 +04:00
parent f5abe71123
commit f658544666
7 changed files with 142 additions and 7 deletions
@@ -38,7 +38,11 @@ public class JavaFxClassBackedElementDescriptor implements XmlElementDescriptor,
private final String myName;
public JavaFxClassBackedElementDescriptor(String name, XmlTag tag) {
this(name, findPsiClass(name, JavaFXNSDescriptor.parseImports((XmlFile)tag.getContainingFile()), tag, tag.getProject()));
this(name, findPsiClass(name, tag));
}
public static PsiClass findPsiClass(String name, XmlTag tag) {
return findPsiClass(name, JavaFXNSDescriptor.parseImports((XmlFile)tag.getContainingFile()), tag, tag.getProject());
}
public JavaFxClassBackedElementDescriptor(String name, PsiClass aClass) {
@@ -314,8 +318,7 @@ public class JavaFxClassBackedElementDescriptor implements XmlElementDescriptor,
public static PsiMethod findPropertySetter(String attributeName, XmlTag context) {
final String packageName = StringUtil.getPackageName(attributeName);
if (context != null && !StringUtil.isEmptyOrSpaces(packageName)) {
final PsiClass classWithStaticProperty =
findPsiClass(packageName, JavaFXNSDescriptor.parseImports((XmlFile)context.getContainingFile()), context, context.getProject());
final PsiClass classWithStaticProperty = findPsiClass(packageName, context);
if (classWithStaticProperty != null) {
return findPropertySetter(attributeName, classWithStaticProperty);
}
@@ -0,0 +1,35 @@
/*
* 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.lang.xml.XMLLanguage;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFileFactory;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlProcessingInstruction;
/**
* User: anna
*/
public class JavaFxPsiUtil {
static XmlProcessingInstruction createSingleImportInstruction(String qualifiedName, Project project) {
final String importText = "<?import " + qualifiedName + "?>";
final PsiElement child =
PsiFileFactory.getInstance(project).createFileFromText("a.fxml", XMLLanguage.INSTANCE, importText).getFirstChild();
return PsiTreeUtil.findChildOfType(child, XmlProcessingInstruction.class);
}
}
@@ -1,7 +1,21 @@
package org.jetbrains.plugins.javaFX.fxml;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.completion.XmlTagInsertHandler;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.lang.ASTNode;
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.xml.XmlElementDescriptor;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* User: anna
@@ -11,4 +25,52 @@ public class JavaFxTagNameReference extends TagNameReference {
public JavaFxTagNameReference(ASTNode element, boolean startTagFlag) {
super(element, startTagFlag);
}
@NotNull
@Override
public LookupElement[] getVariants() {
final PsiElement element = getElement();
if(!myStartTagFlag){
return super.getVariants();
}
final XmlTag xmlTag = (XmlTag)element;
final List<XmlElementDescriptor>
variants = TagNameReference.<XmlElementDescriptor>getTagNameVariants(xmlTag, Arrays.asList(xmlTag.knownNamespaces()), new ArrayList<String>(), Function.ID);
final List<LookupElement> elements = new ArrayList<LookupElement>(variants.size());
for (XmlElementDescriptor descriptor : variants) {
LookupElementBuilder lookupElement = LookupElementBuilder.create(descriptor, descriptor.getName(element));
elements.add(lookupElement.withInsertHandler(JavaFxTagInsertHandler.INSTANCE));
}
return elements.toArray(new LookupElement[elements.size()]);
}
private static class JavaFxTagInsertHandler extends XmlTagInsertHandler {
public static final JavaFxTagInsertHandler INSTANCE = new JavaFxTagInsertHandler();
@Override
public void handleInsert(InsertionContext context, LookupElement item) {
super.handleInsert(context, item);
final Object object = item.getObject();
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());
}
}
}
}
}
}
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<GridPane xmlns:fx="http://javafx.com/fxml">
<children>
<<caret>
</children>
</GridPane>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Button?>
<GridPane xmlns:fx="http://javafx.com/fxml">
<children>
<Button
</children>
</GridPane>
@@ -61,6 +61,10 @@ public class JavaFxCompletionTest extends CompletionTestCase {
doTest("Button");
}
public void testClassInsertImport() throws Exception {
doTest("Button");
}
public void testStaticPropertiesEnumValue() throws Exception {
doTest();
}
@@ -37,6 +37,7 @@ import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlElement;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.NullableFunction;
import com.intellij.util.containers.ContainerUtil;
@@ -237,6 +238,20 @@ public class TagNameReference implements PsiReference {
final Collection<String> namespaces,
@Nullable List<String> nsInfo) {
final List<String> variants = getTagNameVariants(element, namespaces, nsInfo, new Function<XmlElementDescriptor, String>() {
@Override
public String fun(XmlElementDescriptor descriptor) {
return descriptor.getName(element);
}
});
return ArrayUtil.toStringArray(variants);
}
public static <T> List<T> getTagNameVariants(final XmlTag element,
final Collection<String> namespaces,
@Nullable List<String> nsInfo,
final Function<XmlElementDescriptor, T> f) {
XmlElementDescriptor elementDescriptor = null;
String elementNamespace = null;
@@ -279,8 +294,8 @@ public class TagNameReference implements PsiReference {
}
final boolean hasPrefix = StringUtil.isNotEmpty(element.getNamespacePrefix());
final List<String> list = ContainerUtil.mapNotNull(variants, new NullableFunction<XmlElementDescriptor, String>() {
public String fun(XmlElementDescriptor descriptor) {
return ContainerUtil.mapNotNull(variants, new NullableFunction<XmlElementDescriptor, T>() {
public T fun(XmlElementDescriptor descriptor) {
if (descriptor instanceof AnyXmlElementDescriptor) {
return null;
}
@@ -289,10 +304,9 @@ public class TagNameReference implements PsiReference {
return null;
}
return descriptor.getName(element);
return f.fun(descriptor);
}
});
return ArrayUtil.toStringArray(list);
}
private static void processVariantsInNamespace(final String namespace,