Microdata: WEB-6805 HTML microdata: no completion if the element referenced via itemref has upper nesting level than the element with the itemscope attribute

This commit is contained in:
Fedor Korotkov
2013-02-25 21:11:50 +04:00
parent 3e35b220b7
commit 64e0e50630
3 changed files with 57 additions and 33 deletions
@@ -115,7 +115,7 @@ public class MicrodataAttributeDescriptorsProvider implements XmlAttributeDescri
@Override
public String[] getEnumeratedValues() {
final XmlTag scopeParent = findScopeTag(myContext.getParentTag());
final XmlTag scopeParent = findScopeTag(myContext);
return scopeParent != null ? findProperties(scopeParent) : super.getEnumeratedValues();
}
@@ -22,7 +22,6 @@ import com.intellij.codeInsight.daemon.impl.quickfix.ManuallySetupExtResourceAct
import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
@@ -37,17 +36,14 @@ import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.PairFunction;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.StringTokenizer;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
* @author: Fedor.Korotkov
@@ -68,39 +64,39 @@ public class MicrodataUtil {
}
@Nullable
public static XmlTag findScopeTag(@Nullable XmlTag tag) {
public static XmlTag findScopeTag(@Nullable XmlTag context) {
Map<String, XmlTag> id2tag = findScopesWithItemRef(context != null ? context.getContainingFile() : null);
XmlTag tag = context;
while (tag != null) {
if (tag.getAttribute(ITEM_SCOPE) != null) return tag;
XmlTag scopeTag = findInRefsById(tag);
if (scopeTag != null) return scopeTag;
if (tag != context && tag.getAttribute(ITEM_SCOPE) != null) return tag;
final String id = getStripedAttributeValue(tag, "id");
if (id != null && id2tag.containsKey(id)) return id2tag.get(id);
tag = tag.getParentTag();
}
return null;
}
@Nullable
private static XmlTag findInRefsById(XmlTag tag) {
XmlAttribute idAttr = tag.getAttribute("id");
final XmlAttributeValue idValue = idAttr != null ? idAttr.getValueElement() : null;
if (idValue == null) {
return null;
}
final String idToFind = StringUtil.stripQuotesAroundValue(idValue.getText());
XmlTag parentTag = tag.getParentTag();
while (parentTag != null) {
XmlTag scopeTag = ContainerUtil.find(parentTag.getSubTags(), new Condition<XmlTag>() {
@Override
public boolean value(XmlTag tag) {
String refValue = tag.getAttributeValue(ITEM_REF);
return refValue != null && refValue.contains(idToFind);
private static Map<String, XmlTag> findScopesWithItemRef(@Nullable PsiFile file) {
if (!(file instanceof XmlFile)) return Collections.emptyMap();
final Map<String, XmlTag> result = new THashMap<String, XmlTag>();
file.accept(new XmlRecursiveElementVisitor() {
@Override
public void visitXmlTag(final XmlTag tag) {
super.visitXmlTag(tag);
XmlAttribute refAttr = tag.getAttribute(ITEM_REF);
if (refAttr != null && tag.getAttribute(ITEM_SCOPE) != null) {
getReferencesForAttributeValue(refAttr.getValueElement(), new PairFunction<String, Integer, PsiReference>() {
@Nullable
@Override
public PsiReference fun(String t, Integer v) {
result.put(t, tag);
return null;
}
});
}
});
if (scopeTag != null) {
return scopeTag;
}
parentTag = parentTag.getParentTag();
}
return null;
});
return result;
}
public static List<String> extractProperties(PsiFile file, String type) {
@@ -151,8 +147,11 @@ public class MicrodataUtil {
});
}
public static PsiReference[] getReferencesForAttributeValue(XmlAttributeValue element,
public static PsiReference[] getReferencesForAttributeValue(@Nullable XmlAttributeValue element,
PairFunction<String, Integer, PsiReference> refFun) {
if (element == null) {
return PsiReference.EMPTY_ARRAY;
}
String text = element.getText();
String urls = StringUtil.stripQuotesAroundValue(text);
StringTokenizer tokenizer = new StringTokenizer(urls);
@@ -214,4 +214,29 @@ public class MicrodataCompletionTest extends CodeInsightFixtureTestCase {
"name", "nickname", "photo", "title", "role", "url", "affiliation", "friend", "acquaintance", "address"
);
}
public void testPropValueNestedScopesDifferentTrees() throws Throwable {
final VirtualFile personFile = myFixture.copyFileToProject("Person.html");
final VirtualFile addressFile = myFixture.copyFileToProject("Address.html");
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
ExternalResourceManager.getInstance().addResource("http://data-vocabulary.org/Person", personFile.getPath());
ExternalResourceManager.getInstance().addResource("http://data-vocabulary.org/Address", addressFile.getPath());
}
});
doTestInHtml("<div itemscope itemtype=\"http://data-vocabulary.org/Person\" >\n" +
" name is <span itemprop=\"name\">ann</span>\n" +
" role is <span itemprop=\"role\">smth</span>\n" +
" <span itemprop=\"address\" itemscope\n" +
" itemtype=\"http://data-vocabulary.org/Address\" itemref=\"qq\">\n" +
" <span itemprop=\"locality\">spb</span>\n" +
" </span>\n" +
"</div>\n" +
"<div>\n" +
" <span id=\"qq\" itemprop=\"<caret>\">russia</span>\n" +
"</div>",
"street-address", "locality", "region", "postal-code", "country-name"
);
}
}