Skip to content

Commit 837a50f

Browse files
committed
feature: add props completion and navigation
1 parent c4262a6 commit 837a50f

File tree

9 files changed

+405
-10
lines changed

9 files changed

+405
-10
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.axellience.vuegwtplugin.codeinsight.attributes;
2+
3+
import com.axellience.vuegwtplugin.VueGWTIcons;
4+
import com.intellij.psi.PsiElement;
5+
import com.intellij.psi.PsiField;
6+
import com.intellij.psi.meta.PsiPresentableMetaData;
7+
import com.intellij.util.ArrayUtil;
8+
import com.intellij.xml.impl.BasicXmlAttributeDescriptor;
9+
import javax.swing.Icon;
10+
import org.jetbrains.annotations.Nullable;
11+
12+
public class VueGWTAttributeDescriptor extends BasicXmlAttributeDescriptor implements
13+
PsiPresentableMetaData {
14+
15+
private final PsiField field;
16+
private final String name;
17+
private boolean isRequired;
18+
19+
public VueGWTAttributeDescriptor(PsiField field) {
20+
this(field, field.getName());
21+
}
22+
23+
public VueGWTAttributeDescriptor(PsiField field, String name) {
24+
this.field = field;
25+
this.name = name;
26+
this.isRequired = false;
27+
}
28+
29+
@Override
30+
public boolean isRequired() {
31+
return isRequired;
32+
}
33+
34+
@Override
35+
public PsiElement getDeclaration() {
36+
return field;
37+
}
38+
39+
@Override
40+
public String getName() {
41+
return name;
42+
}
43+
44+
@Override
45+
public void init(PsiElement element) {
46+
47+
}
48+
49+
@Override
50+
public boolean hasIdType() {
51+
return false;
52+
}
53+
54+
@Override
55+
public boolean hasIdRefType() {
56+
return false;
57+
}
58+
59+
@Override
60+
public boolean isEnumerated() {
61+
return false;
62+
}
63+
64+
@Override
65+
public boolean isFixed() {
66+
return false;
67+
}
68+
69+
@Override
70+
public String getDefaultValue() {
71+
return null;
72+
}
73+
74+
@Override
75+
public String[] getEnumeratedValues() {
76+
return ArrayUtil.EMPTY_STRING_ARRAY;
77+
}
78+
79+
@Nullable
80+
@Override
81+
public String getTypeName() {
82+
return null;
83+
}
84+
85+
@Nullable
86+
@Override
87+
public Icon getIcon() {
88+
return VueGWTIcons.VUE;
89+
}
90+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.axellience.vuegwtplugin.codeinsight.attributes;
2+
3+
import com.intellij.psi.xml.XmlTag;
4+
import com.intellij.xml.XmlAttributeDescriptor;
5+
import com.intellij.xml.XmlAttributeDescriptorsProvider;
6+
import org.jetbrains.annotations.Nullable;
7+
8+
public class VueGWTAttributeProvider implements XmlAttributeDescriptorsProvider {
9+
10+
@Override
11+
public XmlAttributeDescriptor[] getAttributeDescriptors(XmlTag context) {
12+
return new XmlAttributeDescriptor[0];
13+
}
14+
15+
@Nullable
16+
@Override
17+
public XmlAttributeDescriptor getAttributeDescriptor(String attributeName, XmlTag context) {
18+
return null;
19+
}
20+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.axellience.vuegwtplugin.codeinsight.attributes;
2+
3+
import static com.intellij.patterns.PlatformPatterns.psiElement;
4+
import static com.intellij.patterns.XmlPatterns.xmlAttribute;
5+
6+
import com.intellij.codeInsight.completion.CompletionContributor;
7+
import com.intellij.codeInsight.completion.CompletionType;
8+
import com.intellij.psi.xml.XmlTokenType;
9+
10+
public class VueGWTCompletionContributor extends CompletionContributor {
11+
12+
public VueGWTCompletionContributor() {
13+
this.extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_NAME).withParent(xmlAttribute()),
14+
new VueGWTCompletionProvider());
15+
}
16+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.axellience.vuegwtplugin.codeinsight.attributes;
2+
3+
import com.axellience.vuegwtplugin.VueGWTIcons;
4+
import com.axellience.vuegwtplugin.codeinsight.tags.VueGWTElementDescriptor;
5+
import com.intellij.codeInsight.completion.CompletionParameters;
6+
import com.intellij.codeInsight.completion.CompletionProvider;
7+
import com.intellij.codeInsight.completion.CompletionResultSet;
8+
import com.intellij.codeInsight.lookup.LookupElementBuilder;
9+
import com.intellij.psi.PsiElement;
10+
import com.intellij.psi.PsiField;
11+
import com.intellij.psi.SmartPointerManager;
12+
import com.intellij.psi.xml.XmlAttribute;
13+
import com.intellij.util.ProcessingContext;
14+
import kotlin.Pair;
15+
import org.jetbrains.annotations.NotNull;
16+
17+
public class VueGWTCompletionProvider extends CompletionProvider<CompletionParameters> {
18+
19+
@Override
20+
protected void addCompletions(@NotNull CompletionParameters parameters,
21+
@NotNull ProcessingContext context, @NotNull CompletionResultSet result) {
22+
PsiElement parent = parameters.getPosition().getParent();
23+
if (!(parent instanceof XmlAttribute)) {
24+
return;
25+
}
26+
27+
XmlAttribute attribute = (XmlAttribute) parent;
28+
if (!(attribute.getParent().getDescriptor() instanceof VueGWTElementDescriptor)) {
29+
return;
30+
}
31+
32+
String prefix = result.getPrefixMatcher().getPrefix();
33+
CompletionResultSet newResults =
34+
prefix.equals("v-bind:") ? result.withPrefixMatcher("") : result;
35+
String lookupItemPrefix = prefix.startsWith(":") ? ":" : "";
36+
37+
((VueGWTElementDescriptor) attribute.getParent().getDescriptor())
38+
.getProps()
39+
.forEach(prop -> {
40+
PsiField field = (PsiField) prop.getDeclaration();
41+
newResults.addElement(
42+
LookupElementBuilder.create(
43+
new Pair<>(
44+
lookupItemPrefix + field.getName(),
45+
SmartPointerManager
46+
.getInstance(field.getProject())
47+
.createSmartPsiElementPointer(field)),
48+
lookupItemPrefix + field.getName()
49+
)
50+
.withIcon(VueGWTIcons.VUE)
51+
);
52+
});
53+
}
54+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package com.axellience.vuegwtplugin.codeinsight.tags;
2+
3+
import com.axellience.vuegwtplugin.codeinsight.attributes.VueGWTAttributeDescriptor;
4+
import com.axellience.vuegwtplugin.util.VueGWTPluginUtil;
5+
import com.intellij.psi.PsiClass;
6+
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.impl.source.html.dtd.HtmlNSDescriptorImpl;
8+
import com.intellij.psi.impl.source.xml.XmlDescriptorUtil;
9+
import com.intellij.psi.xml.XmlAttribute;
10+
import com.intellij.psi.xml.XmlTag;
11+
import com.intellij.xml.XmlAttributeDescriptor;
12+
import com.intellij.xml.XmlElementDescriptor;
13+
import com.intellij.xml.XmlElementsGroup;
14+
import com.intellij.xml.XmlNSDescriptor;
15+
import java.util.Arrays;
16+
import java.util.LinkedList;
17+
import java.util.List;
18+
import java.util.stream.Collectors;
19+
import org.jetbrains.annotations.Nullable;
20+
21+
public class VueGWTElementDescriptor implements XmlElementDescriptor {
22+
23+
private final PsiClass componentClass;
24+
25+
public VueGWTElementDescriptor(PsiClass componentClass) {
26+
this.componentClass = componentClass;
27+
}
28+
29+
@Override
30+
public XmlAttributeDescriptor[] getAttributesDescriptors(@Nullable XmlTag context) {
31+
List<XmlAttributeDescriptor> result = Arrays
32+
.stream(HtmlNSDescriptorImpl.getCommonAttributeDescriptors(context))
33+
.collect(Collectors.toCollection(LinkedList::new));
34+
result.addAll(getProps());
35+
return result.toArray(new XmlAttributeDescriptor[0]);
36+
}
37+
38+
@Nullable
39+
@Override
40+
public XmlAttributeDescriptor getAttributeDescriptor(String attributeName,
41+
@Nullable XmlTag context) {
42+
return Arrays.stream(componentClass.getAllFields())
43+
.filter(
44+
field -> field.getName().equals(attributeName) ||
45+
(":" + field.getName()).equals(attributeName) ||
46+
("v-bind:" + field.getName()).equals(attributeName)
47+
)
48+
.findFirst()
49+
.map(field -> (XmlAttributeDescriptor) new VueGWTAttributeDescriptor(field))
50+
.orElse(HtmlNSDescriptorImpl.getCommonAttributeDescriptor(attributeName, context));
51+
}
52+
53+
@Nullable
54+
@Override
55+
public XmlAttributeDescriptor getAttributeDescriptor(XmlAttribute attribute) {
56+
return getAttributeDescriptor(attribute.getName(), attribute.getParent());
57+
}
58+
59+
public List<XmlAttributeDescriptor> getProps() {
60+
return Arrays.stream(componentClass.getAllFields())
61+
.map(VueGWTAttributeDescriptor::new)
62+
.collect(Collectors.toList());
63+
}
64+
65+
@Override
66+
public int getContentType() {
67+
return XmlElementDescriptor.CONTENT_TYPE_ANY;
68+
}
69+
70+
@Override
71+
public PsiElement getDeclaration() {
72+
return componentClass;
73+
}
74+
75+
@Nullable
76+
@Override
77+
public XmlElementDescriptor getElementDescriptor(XmlTag childTag, XmlTag contextTag) {
78+
return XmlDescriptorUtil.getElementDescriptor(childTag, contextTag);
79+
}
80+
81+
@Override
82+
public XmlElementDescriptor[] getElementsDescriptors(XmlTag context) {
83+
return XmlDescriptorUtil.getElementsDescriptors(context);
84+
}
85+
86+
@Override
87+
public String getDefaultName() {
88+
return this.getName();
89+
}
90+
91+
@Override
92+
public String getName(PsiElement context) {
93+
if (context instanceof XmlTag) {
94+
return ((XmlTag) context).getName();
95+
}
96+
;
97+
return this.getName();
98+
}
99+
100+
@Override
101+
public String getName() {
102+
return VueGWTPluginUtil.componentToTagName(componentClass);
103+
}
104+
105+
@Override
106+
public String getQualifiedName() {
107+
return this.getName();
108+
}
109+
110+
@Override
111+
public void init(PsiElement element) {
112+
113+
}
114+
115+
@Nullable
116+
@Override
117+
public String getDefaultValue() {
118+
return null;
119+
}
120+
121+
@Nullable
122+
@Override
123+
public XmlElementsGroup getTopGroup() {
124+
return null;
125+
}
126+
127+
@Nullable
128+
@Override
129+
public XmlNSDescriptor getNSDescriptor() {
130+
return null;
131+
}
132+
}

src/main/java/com/axellience/vuegwtplugin/tags/VueGWTTagProvider.java renamed to src/main/java/com/axellience/vuegwtplugin/codeinsight/tags/VueGWTTagProvider.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.axellience.vuegwtplugin.tags;
1+
package com.axellience.vuegwtplugin.codeinsight.tags;
22

33
import com.axellience.vuegwtplugin.VueGWTIcons;
44
import com.axellience.vuegwtplugin.util.VueGWTComponentAnnotationUtil;
@@ -7,17 +7,40 @@
77
import com.intellij.codeInsight.lookup.LookupElementBuilder;
88
import com.intellij.psi.PsiClass;
99
import com.intellij.psi.PsiClassType;
10+
import com.intellij.psi.PsiFile;
1011
import com.intellij.psi.PsiJavaFile;
1112
import com.intellij.psi.SmartPointerManager;
13+
import com.intellij.psi.impl.source.xml.XmlElementDescriptorProvider;
1214
import com.intellij.psi.xml.XmlTag;
15+
import com.intellij.xml.XmlElementDescriptor;
1316
import com.intellij.xml.XmlTagNameProvider;
1417
import java.util.List;
1518
import java.util.Optional;
1619
import java.util.stream.Collectors;
1720
import kotlin.Pair;
1821
import org.jetbrains.annotations.NotNull;
22+
import org.jetbrains.annotations.Nullable;
1923

20-
public class VueGWTTagProvider implements XmlTagNameProvider {
24+
public class VueGWTTagProvider implements XmlElementDescriptorProvider, XmlTagNameProvider {
25+
26+
@Nullable
27+
@Override
28+
public XmlElementDescriptor getDescriptor(XmlTag tag) {
29+
String tagName = tag.getName(); // Tag name of the current element
30+
31+
PsiFile templateFile = tag.getContainingFile();
32+
33+
return VueGWTPluginUtil
34+
.findJavaFromTemplate(templateFile)
35+
.flatMap(VueGWTComponentAnnotationUtil::getComponentAnnotationFromJavaFile)
36+
.flatMap(
37+
annotation -> VueGWTComponentAnnotationUtil
38+
.getImportedComponentClassTypeFromComponentAnnotation(annotation, tagName)
39+
)
40+
.map(PsiClassType::resolve)
41+
.map(VueGWTElementDescriptor::new)
42+
.orElse(null);
43+
}
2144

2245
@Override
2346
public void addTagNameVariants(List<LookupElement> elements, @NotNull XmlTag tag, String prefix) {

0 commit comments

Comments
 (0)