IDEA-76891 provide references to packages from android xml files

This commit is contained in:
Eugene Kudelevsky
2012-07-17 20:19:27 +04:00
parent d95a4eeb61
commit 96d99527f6
40 changed files with 529 additions and 45 deletions
+3
View File
@@ -195,6 +195,9 @@
<projectService serviceInterface="org.jetbrains.android.maven.AndroidExternalApklibDependenciesManager"
serviceImplementation="org.jetbrains.android.maven.AndroidExternalApklibDependenciesManager"/>
<xml.xmlExtension implementation="org.jetbrains.android.dom.AndroidXmlExtension" order="first"/>
<psi.referenceProvider language="XML" providerClass="org.jetbrains.android.dom.AndroidXmlReferenceProvider">
<pattern type="xml">xmlTag()</pattern>
</psi.referenceProvider>
<fileBasedIndex implementation="org.jetbrains.android.AndroidIdIndex"/>
<fileBasedIndex implementation="org.jetbrains.android.AndroidValueResourcesIndex"/>
<referencesSearch implementation="org.jetbrains.android.AndroidReferenceSearchExecutor"/>
@@ -19,8 +19,11 @@ import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.xml.XmlChildRole;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import org.jetbrains.android.dom.animation.AndroidAnimationUtils;
@@ -52,42 +55,34 @@ public class AndroidCompletionContributor extends CompletionContributor {
}
}
private static boolean complete(@NotNull AndroidFacet facet, PsiElement position, CompletionResultSet resultSet) {
PsiElement parent = position.getParent();
if (parent instanceof XmlTag) {
XmlTag tag = (XmlTag)parent;
if (tag.getParentTag() == null) {
PsiFile file = tag.getContainingFile().getOriginalFile();
if (file instanceof XmlFile) {
XmlFile xmlFile = (XmlFile)file;
if (ManifestDomFileDescription.isManifestFile(xmlFile)) {
resultSet.addElement(LookupElementBuilder.create("manifest"));
return false;
}
else if (LayoutDomFileDescription.isLayoutFile(xmlFile)) {
addAll(AndroidLayoutUtil.getPossibleRoots(facet), resultSet);
return false;
}
else if (AnimationDomFileDescription.isAnimationFile(xmlFile)) {
addAll(AndroidAnimationUtils.getPossibleChildren(facet), resultSet);
return false;
}
else if (AnimatorDomFileDescription.isAnimatorFile(xmlFile)) {
addAll(AndroidAnimatorUtil.getPossibleChildren(), resultSet);
return false;
}
else if (XmlResourceDomFileDescription.isXmlResourceFile(xmlFile)) {
addAll(AndroidXmlResourcesUtil.getPossibleRoots(facet), resultSet);
return false;
}
else if (AndroidDrawableDomUtil.isDrawableResourceFile(xmlFile)) {
addAll(AndroidDrawableDomUtil.getPossibleRoots(), resultSet);
}
else if (ColorDomFileDescription.isColorResourceFile(xmlFile)) {
addAll(Arrays.asList(DrawableStateListDomFileDescription.SELECTOR_TAG_NAME), resultSet);
}
}
}
private static boolean complete(@NotNull AndroidFacet facet, @NotNull XmlFile xmlFile, @NotNull CompletionResultSet resultSet) {
if (ManifestDomFileDescription.isManifestFile(xmlFile)) {
resultSet.addElement(LookupElementBuilder.create("manifest"));
return false;
}
else if (LayoutDomFileDescription.isLayoutFile(xmlFile)) {
addAll(AndroidLayoutUtil.getPossibleRoots(facet), resultSet);
return false;
}
else if (AnimationDomFileDescription.isAnimationFile(xmlFile)) {
addAll(AndroidAnimationUtils.getPossibleChildren(facet), resultSet);
return false;
}
else if (AnimatorDomFileDescription.isAnimatorFile(xmlFile)) {
addAll(AndroidAnimatorUtil.getPossibleChildren(), resultSet);
return false;
}
else if (XmlResourceDomFileDescription.isXmlResourceFile(xmlFile)) {
addAll(AndroidXmlResourcesUtil.getPossibleRoots(facet), resultSet);
return false;
}
else if (AndroidDrawableDomUtil.isDrawableResourceFile(xmlFile)) {
addAll(AndroidDrawableDomUtil.getPossibleRoots(), resultSet);
return false;
}
else if (ColorDomFileDescription.isColorResourceFile(xmlFile)) {
addAll(Arrays.asList(DrawableStateListDomFileDescription.SELECTOR_TAG_NAME), resultSet);
return false;
}
return true;
}
@@ -96,8 +91,43 @@ public class AndroidCompletionContributor extends CompletionContributor {
public void fillCompletionVariants(CompletionParameters parameters, CompletionResultSet resultSet) {
PsiElement position = parameters.getPosition();
AndroidFacet facet = AndroidFacet.getInstance(position);
if (facet == null) return;
if (!complete(facet, position, resultSet)) {
if (facet == null) {
return;
}
PsiElement parent = position.getParent();
if (!(parent instanceof XmlTag)) {
return;
}
XmlTag tag = (XmlTag)parent;
if (tag.getParentTag() != null) {
return;
}
final ASTNode startTagName = XmlChildRole.START_TAG_NAME_FINDER.findChild(tag.getNode());
if (startTagName == null || startTagName.getPsi() != position) {
return;
}
final PsiFile file = tag.getContainingFile();
if (!(file instanceof XmlFile)) {
return;
}
final PsiReference reference = file.findReferenceAt(parameters.getOffset());
if (reference != null) {
final PsiElement element = reference.getElement();
if (element != null) {
final int refOffset = element.getTextRange().getStartOffset() +
reference.getRangeInElement().getStartOffset();
if (refOffset != position.getTextRange().getStartOffset()) {
// do not provide completion if we're inside some reference starting in the middle of tag name
return;
}
}
}
if (!complete(facet, (XmlFile)file, resultSet)) {
resultSet.stopHere();
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.android.dom;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.xml.XmlElementDescriptorProvider;
@@ -32,6 +33,7 @@ import org.jetbrains.android.dom.xml.XmlResourceElement;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.SimpleClassMapConstructor;
import org.jetbrains.android.util.AndroidUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
@@ -45,7 +47,7 @@ import java.util.Map;
*/
public class AndroidDomElementDescriptorProvider implements XmlElementDescriptorProvider {
@Nullable
private static XmlElementDescriptor getDescriptor(DomElement domElement, XmlTag tag, String baseClassName) {
private static XmlElementDescriptor getDescriptor(DomElement domElement, XmlTag tag, @Nullable String baseClassName) {
AndroidFacet facet = AndroidFacet.getInstance(domElement);
if (facet == null) return null;
PsiClass aClass;
@@ -78,6 +80,15 @@ public class AndroidDomElementDescriptorProvider implements XmlElementDescriptor
}
public XmlElementDescriptor getDescriptor(XmlTag tag) {
final Pair<AndroidDomElement, String> pair = getDomElementAndBaseClassQName(tag);
if (pair == null) {
return null;
}
return getDescriptor(pair.getFirst(), tag, pair.getSecond());
}
@Nullable
public static Pair<AndroidDomElement, String> getDomElementAndBaseClassQName(@NotNull XmlTag tag) {
Project project = tag.getProject();
if (project.isDefault()) return null;
final DomElement domElement = DomManager.getDomManager(project).getDomElement(tag);
@@ -92,6 +103,6 @@ public class AndroidDomElementDescriptorProvider implements XmlElementDescriptor
else if (domElement instanceof XmlResourceElement) {
className = AndroidXmlResourcesUtil.PREFERENCE_CLASS_NAME;
}
return getDescriptor(domElement, tag, className);
return Pair.create((AndroidDomElement)domElement, className);
}
}
@@ -26,14 +26,18 @@ import com.intellij.xml.DefaultXmlExtension;
import org.jetbrains.android.dom.manifest.ManifestDomFileDescription;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.util.AndroidResourceUtil;
import org.jetbrains.annotations.Nullable;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidXmlExtension extends DefaultXmlExtension {
@Nullable
@Override
public TagNameReference createTagNameReference(ASTNode nameElement, boolean startTagFlag) {
return new AndroidClassTagNameReference(nameElement, startTagFlag);
return AndroidXmlReferenceProvider.areReferencesProvidedByReferenceProvider(nameElement)
? null
: new AndroidClassTagNameReference(nameElement, startTagFlag);
}
@Override
@@ -0,0 +1,232 @@
package org.jetbrains.android.dom;
import com.intellij.codeInsight.completion.JavaLookupElementBuilder;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.ResolveCache;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlChildRole;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ArrayUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ProcessingContext;
import com.intellij.util.Processor;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidXmlReferenceProvider extends PsiReferenceProvider {
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
if (!(element instanceof XmlTag)) {
return PsiReference.EMPTY_ARRAY;
}
final Module module = ModuleUtilCore.findModuleForPsiElement(element);
if (module == null || AndroidFacet.getInstance(module) == null) {
return PsiReference.EMPTY_ARRAY;
}
final ASTNode startTagName = XmlChildRole.START_TAG_NAME_FINDER.findChild(element.getNode());
final String baseClassQName = computeBaseClass((XmlTag)element);
if (baseClassQName == null) {
return PsiReference.EMPTY_ARRAY;
}
final List<PsiReference> result = new ArrayList<PsiReference>();
final XmlTag tag = (XmlTag)element;
if (startTagName != null && areReferencesProvidedByReferenceProvider(startTagName)) {
addReferences(tag, startTagName.getPsi(), result, module, baseClassQName, true);
}
final ASTNode closingTagName = XmlChildRole.CLOSING_TAG_NAME_FINDER.findChild(element.getNode());
if (closingTagName != null && areReferencesProvidedByReferenceProvider(closingTagName)) {
addReferences(tag, closingTagName.getPsi(), result, module, baseClassQName, false);
}
return result.toArray(new PsiReference[result.size()]);
}
private static void addReferences(@NotNull XmlTag tag,
@NotNull PsiElement nameElement,
@NotNull List<PsiReference> result,
@NotNull Module module,
@NotNull String baseClassQName,
boolean startTag) {
final String text = nameElement.getText();
if (text == null) {
return;
}
final String[] nameParts = text.split("\\.");
if (nameParts.length == 0) {
return;
}
int offset = 0;
for (int i = 0; i < nameParts.length; i++) {
final String name = nameParts[i];
if (name.length() > 0) {
offset += name.length();
final TextRange range = new TextRange(offset - name.length(), offset);
final boolean isPackage = i < nameParts.length - 1;
result.add(new MyClassOrPackageReference(tag, nameElement, range, isPackage, module, baseClassQName, startTag));
}
offset++;
}
}
public static boolean areReferencesProvidedByReferenceProvider(ASTNode nameElement) {
if (nameElement != null) {
final PsiElement psiNameElement = nameElement.getPsi();
final XmlTag tag = psiNameElement != null
? PsiTreeUtil.getParentOfType(psiNameElement, XmlTag.class)
: null;
if (tag != null) {
final String baseClassQName = computeBaseClass(tag);
if (baseClassQName != null) {
final String text = nameElement.getText();
return text != null && text.contains(".");
}
}
}
return false;
}
@Nullable
private static String computeBaseClass(XmlTag context) {
final XmlTag parentTag = context.getParentTag();
final Pair<AndroidDomElement, String> pair =
AndroidDomElementDescriptorProvider.getDomElementAndBaseClassQName(parentTag != null ? parentTag : context);
return pair != null ? pair.getSecond() : null;
}
private static class MyClassOrPackageReference extends PsiReferenceBase<PsiElement> {
private final PsiElement myNameElement;
private final TextRange myRangeInNameElement;
private final boolean myIsPackage;
private final Module myModule;
private final String myBaseClassQName;
private final boolean myStartTag;
public MyClassOrPackageReference(@NotNull XmlTag tag,
@NotNull PsiElement nameElement,
@NotNull TextRange rangeInNameElement,
boolean isPackage,
@NotNull Module module,
@NotNull String baseClassQName,
boolean startTag) {
super(tag, rangeInParent(nameElement, rangeInNameElement), true);
myNameElement = nameElement;
myRangeInNameElement = rangeInNameElement;
myIsPackage = isPackage;
myModule = module;
myBaseClassQName = baseClassQName;
myStartTag = startTag;
}
private static TextRange rangeInParent(PsiElement element, TextRange range) {
final int offset = element.getStartOffsetInParent();
return new TextRange(range.getStartOffset() + offset, range.getEndOffset() + offset);
}
@Override
public PsiElement resolve() {
return ResolveCache.getInstance(myElement.getProject()).resolveWithCaching(this, new ResolveCache.Resolver() {
@Nullable
@Override
public PsiElement resolve(@NotNull PsiReference reference, boolean incompleteCode) {
return resolveInner();
}
}, false, false);
}
@Nullable
private PsiElement resolveInner() {
final int end = myRangeInNameElement.getEndOffset();
final String value = myNameElement.getText().substring(0, end);
final JavaPsiFacade facade = JavaPsiFacade.getInstance(myElement.getProject());
return myIsPackage ?
facade.findPackage(value) :
facade.findClass(value, myModule.getModuleWithDependenciesAndLibrariesScope(false));
}
@NotNull
@Override
public Object[] getVariants() {
final String prefix = myNameElement.getText().substring(0, myRangeInNameElement.getStartOffset());
if (!myStartTag) {
final ASTNode startTagNode = XmlChildRole.START_TAG_NAME_FINDER.findChild(myElement.getNode());
if (startTagNode != null) {
final String startTagName = startTagNode.getText();
if (startTagName != null) {
if (startTagName.startsWith(prefix)) {
return new Object[]{startTagName.substring(prefix.length())};
}
}
}
return EMPTY_ARRAY;
}
final Project project = myModule.getProject();
final PsiClass baseClass =
JavaPsiFacade.getInstance(project).findClass(myBaseClassQName, myModule.getModuleWithDependenciesAndLibrariesScope(false));
if (baseClass == null) {
return EMPTY_ARRAY;
}
final List<Object> result = new ArrayList<Object>();
ClassInheritorsSearch.search(baseClass, myModule.getModuleWithDependenciesScope(), true, true, false).forEach(
new Processor<PsiClass>() {
@Override
public boolean process(PsiClass psiClass) {
if (psiClass.getContainingClass() != null) {
return true;
}
String name = psiClass.getQualifiedName();
if (name != null && name.startsWith(prefix)) {
name = name.substring(prefix.length());
result.add(JavaLookupElementBuilder.forClass(psiClass, name, true));
}
return true;
}
});
return ArrayUtil.toObjectArray(result);
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
final String newName = myIsPackage
? ((PsiPackage)element).getQualifiedName()
: ((PsiClass)element).getQualifiedName();
final ElementManipulator<PsiElement> manipulator = ElementManipulators.getManipulator(myNameElement);
final TextRange range = new TextRange(0, myRangeInNameElement.getEndOffset());
return manipulator != null ? manipulator.handleContentChange(myNameElement, range, newName) : element;
}
@Nullable
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
final ElementManipulator<PsiElement> manipulator = ElementManipulators.getManipulator(myNameElement);
assert manipulator != null : "Cannot find manipulator for " + myNameElement;
return manipulator.handleContentChange(myNameElement, myRangeInNameElement, newElementName);
}
}
}
@@ -0,0 +1,6 @@
package p1.p2;
import android.view.View;
public class IncorrectView {
}
@@ -0,0 +1,6 @@
package p1.p2;
import android.view.View;
public class LabelView1 extends View {
}
@@ -0,0 +1,3 @@
<LinearLayout>
<p1.<caret>
</LinearLayout>
@@ -0,0 +1,2 @@
<p1.p2.LabelView>
</p1.p2.<caret>>
@@ -0,0 +1,3 @@
<LinearLayout>
<<caret>
</LinearLayout>
@@ -0,0 +1,3 @@
<LinearLayout>
<p1.p2.LabelView
</LinearLayout>
@@ -0,0 +1 @@
<p1.p2.Lab<caret> xmlns:android="http://schemas.android.com/apk/res/android"/>
@@ -0,0 +1 @@
<p1.p2.LabelView/>
@@ -0,0 +1 @@
<p1.p2.<caret> xmlns:android="http://schemas.android.com/apk/res/android"/>
@@ -0,0 +1 @@
<<caret> xmlns:android="http://schemas.android.com/apk/res/android"/>
@@ -0,0 +1 @@
<p1.p2.LabelView/>
@@ -0,0 +1,4 @@
<LinearLayout>
<p1.p2.LabelView>
</<caret>>
</LinearLayout>
@@ -0,0 +1,4 @@
<LinearLayout>
<p1.p2.LabelView>
</p1.p2.LabelView>
</LinearLayout>
@@ -0,0 +1,4 @@
<LinearLayout>
<p1.p2.LabelView>
</p1.p2.La<caret>>
</LinearLayout>
@@ -0,0 +1,4 @@
<LinearLayout>
<p1.p2.LabelView>
</p1.p2.<caret>>
</LinearLayout>
@@ -0,0 +1,2 @@
<p1.p2.LabelView>
</<caret>>
@@ -0,0 +1,2 @@
<p1.p2.LabelView>
</p1.p2.LabelView>
@@ -0,0 +1,6 @@
package p1.p2;
import android.preference.CheckBoxPreference;
public class MyPreference extends CheckBoxPreference {
}
@@ -0,0 +1 @@
<p1.p<caret>
@@ -0,0 +1 @@
<p1.p2.MyPreference
@@ -0,0 +1,5 @@
<PreferenceScreen
>
<p1.p<caret>
</PreferenceScreen>
@@ -0,0 +1,5 @@
<PreferenceScreen
>
<p1.p2.MyPreference
</PreferenceScreen>
@@ -0,0 +1,3 @@
package p1.newp;
public class Dummy {}
@@ -0,0 +1,6 @@
package p1.p2;
import android.preference.CheckBoxPreference;
public class MyPreference extends CheckBoxPreference {
}
@@ -0,0 +1,5 @@
<PreferenceScreen
>
<p1.p2.MyPreference/>
</PreferenceScreen>
@@ -0,0 +1,5 @@
<PreferenceScreen
>
<p1.newp.p2.MyPreference/>
</PreferenceScreen>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<p1.p<caret>2.MyWidget xmlns:android="http://schemas.android.com/apk/res/android">
<ListView/>
</p1.p2.MyWidget>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<p1.newPackage.MyWidget>
<ListView/>
</p1.newPackage.MyWidget>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<p1.p<caret>2.MyWidget xmlns:android="http://schemas.android.com/apk/res/android">
<ListView/>
</p1.p2.MyWidget>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<p1.newp.p2.MyWidget>
<ListView/>
</p1.newp.p2.MyWidget>
@@ -306,6 +306,28 @@ public class AndroidRenameTest extends AndroidTestCase {
myFixture.checkResultByFile(BASE_PATH + "layout_widget_after.xml");
}
public void testRenameWidgetPackage1() throws Throwable {
createManifest();
myFixture.copyFileToProject(BASE_PATH + "MyWidget.java", "src/p1/p2/MyWidget.java");
VirtualFile file = myFixture.copyFileToProject(BASE_PATH + "layout_widget1.xml", "res/layout/layout_widget1.xml");
myFixture.configureFromExistingVirtualFile(file);
checkAndRename("newPackage");
myFixture.checkResultByFile(BASE_PATH + "layout_widget1_after.xml");
}
public void testMoveWidgetPackage1() throws Throwable {
createManifest();
myFixture.copyFileToProject(BASE_PATH + "Dummy.java", "src/p1/newp/Dummy.java");
myFixture.copyFileToProject(BASE_PATH + "MyWidget.java", "src/p1/p2/MyWidget.java");
myFixture.copyFileToProject(BASE_PATH + "MyPreference.java", "src/p1/p2/MyPreference.java");
final VirtualFile f = myFixture.copyFileToProject(BASE_PATH + "layout_widget2.xml", "res/layout/layout_widget2.xml");
myFixture.configureFromExistingVirtualFile(f);
myFixture.copyFileToProject(BASE_PATH + "custom_pref.xml", "res/xml/custom_pref.xml");
doMovePackage("p1.p2", "p1.newp");
myFixture.checkResultByFile("res/layout/layout_widget2.xml", BASE_PATH + "layout_widget2_after.xml", false);
myFixture.checkResultByFile("res/xml/custom_pref.xml", BASE_PATH + "custom_pref_after.xml", false);
}
private void doMovePackageTest(String packageName, String newPackageName, String activityPath) throws Exception {
myFixture.copyDirectoryToProject(BASE_PATH + "empty", "src/p1/p2/p3");
myFixture.copyFileToProject(BASE_PATH + "MyActivity.java", activityPath);
@@ -2,6 +2,7 @@ package org.jetbrains.android.dom;
import com.android.sdklib.SdkConstants;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.intention.IntentionAction;
@@ -99,6 +100,68 @@ public class AndroidLayoutDomTest extends AndroidDomTest {
toTestCompletion("ctn.xml", "ctn_after.xml");
}
public void testCustomTagCompletion1() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
copyFileToProject("LabelView1.java", "src/p1/p2/LabelView1.java");
copyFileToProject("IncorrectView.java", "src/p1/p2/IncorrectView.java");
doTestCompletionVariants("ctn1.xml", "p2.LabelView", "p2.LabelView1");
}
public void testCustomTagCompletion2() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
VirtualFile file = copyFileToProject("ctn2.xml");
myFixture.configureFromExistingVirtualFile(file);
myFixture.complete(CompletionType.BASIC);
myFixture.type("p1\n");
myFixture.checkResultByFile(testFolder + '/' + "ctn2_after.xml");
}
public void testCustomTagCompletion3() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
toTestCompletion("ctn3.xml", "ctn3_after.xml");
}
public void testCustomTagCompletion4() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
doTestCompletionVariants("ctn4.xml", "LabelView");
}
public void testCustomTagCompletion5() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
VirtualFile file = copyFileToProject("ctn5.xml");
myFixture.configureFromExistingVirtualFile(file);
myFixture.complete(CompletionType.BASIC);
myFixture.type("p1\n");
myFixture.checkResultByFile(testFolder + '/' + "ctn5_after.xml");
}
public void testCustomTagCompletion6() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
toTestCompletion("ctn6.xml", "ctn6_after.xml");
}
public void testCustomTagCompletion7() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
toTestCompletion("ctn7.xml", "ctn6_after.xml");
}
public void testCustomTagCompletion8() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
copyFileToProject("LabelView1.java", "src/p1/p2/LabelView1.java");
doTestCompletionVariants("ctn8.xml", "LabelView");
}
public void testCustomTagCompletion9() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
toTestCompletion("ctn9.xml", "ctn9_after.xml");
}
public void testCustomTagCompletion10() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
copyFileToProject("LabelView1.java", "src/p1/p2/LabelView1.java");
doTestCompletionVariants("ctn10.xml", "LabelView");
}
public void testCustomAttributeNameCompletion() throws Throwable {
copyFileToProject("LabelView.java", "src/p1/p2/LabelView.java");
doTestCompletionVariants("can.xml", "text", "textColor", "textSize");
@@ -302,7 +365,7 @@ public class AndroidLayoutDomTest extends AndroidDomTest {
myFixture.configureFromExistingVirtualFile(file);
PsiFile psiFile = myFixture.getFile();
String text = psiFile.getText();
int rootOffset = text.indexOf("android.widget.ScrollView");
int rootOffset = text.indexOf("ScrollView");
PsiReference rootReference = psiFile.findReferenceAt(rootOffset);
assertNotNull(rootReference);
PsiElement rootViewClass = rootReference.resolve();
@@ -133,6 +133,16 @@ public class AndroidXmlResourcesDomTest extends AndroidDomTest {
doTestHighlighting();
}
public void testCustomPreference1() throws Throwable {
copyFileToProject("MyPreference.java", "src/p1/p2/MyPreference.java");
toTestCompletion("customPref1.xml", "customPref1_after.xml");
}
public void testCustomPreference2() throws Throwable {
copyFileToProject("MyPreference.java", "src/p1/p2/MyPreference.java");
toTestCompletion("customPref2.xml", "customPref2_after.xml");
}
public void testPreferenceHeaders1() throws Throwable {
doTestCompletion();
}
@@ -150,13 +150,17 @@ public class XmlTagImpl extends XmlElementImpl implements XmlTag {
String prefix = getNamespacePrefix();
TagNameReference startTagRef = TagNameReference.createTagNameReference(this, startTagName, true);
refs.add(startTagRef);
if (startTagRef != null) {
refs.add(startTagRef);
}
if (prefix.length() > 0) {
refs.add(createPrefixReference(startTagName, prefix, startTagRef));
}
if (endTagName != null) {
TagNameReference endTagRef = TagNameReference.createTagNameReference(this, endTagName, false);
refs.add(endTagRef);
if (endTagRef != null) {
refs.add(endTagRef);
}
prefix = XmlUtil.findPrefixByQualifiedName(endTagName.getText());
if (StringUtil.isNotEmpty(prefix)) {
refs.add(createPrefixReference(endTagName, prefix, endTagRef));
@@ -32,7 +32,10 @@ import com.intellij.psi.impl.source.xml.SchemaPrefix;
import com.intellij.psi.impl.source.xml.TagNameReference;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.xml.*;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlDocument;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.IncorrectOperationException;
import com.intellij.xml.util.XmlUtil;
import org.jetbrains.annotations.NonNls;
@@ -78,6 +81,7 @@ public abstract class XmlExtension {
@NotNull
public abstract Set<String> guessUnboundNamespaces(@NotNull PsiElement element, final XmlFile file);
@Nullable
public TagNameReference createTagNameReference(final ASTNode nameElement, final boolean startTagFlag) {
return new TagNameReference(nameElement, startTagFlag);
}