mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-76891 provide references to packages from android xml files
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user