Skip to content

Commit f6ca135

Browse files
committed
XMl completion contributor improvements
- completion for php classes and class elements (properties, methods, constants) - module name completion - file path completion
1 parent d0a4b19 commit f6ca135

21 files changed

+442
-105
lines changed

src/com/magento/idea/magento2plugin/completion/xml/provider/CompositeCompletionProvider.java src/com/magento/idea/magento2plugin/completion/provider/CompositeCompletionProvider.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;

src/com/magento/idea/magento2plugin/completion/xml/provider/EventNameCompletionContributor.java src/com/magento/idea/magento2plugin/completion/provider/EventNameCompletionContributor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.magento.idea.magento2plugin.completion.provider;
2+
3+
import com.intellij.codeInsight.completion.CompletionParameters;
4+
import com.intellij.codeInsight.completion.CompletionProvider;
5+
import com.intellij.codeInsight.completion.CompletionResultSet;
6+
import com.intellij.codeInsight.lookup.LookupElementBuilder;
7+
import com.intellij.icons.AllIcons;
8+
import com.intellij.openapi.vfs.VirtualFile;
9+
import com.intellij.psi.PsiElement;
10+
import com.intellij.util.ProcessingContext;
11+
import com.magento.idea.magento2plugin.util.RegExUtil;
12+
import com.magento.idea.magento2plugin.util.VfsUtil;
13+
import com.magento.idea.magento2plugin.util.magento.FileBasedIndexUtil;
14+
import org.jetbrains.annotations.NotNull;
15+
16+
import java.util.*;
17+
import java.util.regex.Matcher;
18+
import java.util.regex.Pattern;
19+
20+
public class FilePathCompletionProvider extends CompletionProvider<CompletionParameters> {
21+
22+
@Override
23+
protected void addCompletions(@NotNull CompletionParameters parameters,
24+
ProcessingContext context,
25+
@NotNull CompletionResultSet result) {
26+
PsiElement position = parameters.getPosition().getOriginalElement();
27+
if (position == null) {
28+
return;
29+
}
30+
31+
String prefix = result.getPrefixMatcher().getPrefix();
32+
33+
String filePath;
34+
String filePathPrefix;
35+
boolean removeFileExtension = false;
36+
37+
Pattern pattern = Pattern.compile(
38+
"(" + RegExUtil.Magento.MODULE_NAME + "(\\W+))(" + RegExUtil.FILE_PATH + ")?"
39+
);
40+
Matcher matcher = pattern.matcher(prefix);
41+
if (matcher.find()) {
42+
filePathPrefix = matcher.group(1);
43+
removeFileExtension = matcher.group(2).equals("/");
44+
filePath = matcher.group(3) != null ? matcher.group(3) : "";
45+
} else {
46+
pattern = Pattern.compile("(" + RegExUtil.FILE_PATH + ")+");
47+
matcher = pattern.matcher(prefix);
48+
if (!matcher.find()) {
49+
return;
50+
}
51+
filePathPrefix = "";
52+
filePath = matcher.group(1);
53+
}
54+
55+
// find all view virtual files
56+
Collection<VirtualFile> viewVfs = findViewVfs(parameters, result);
57+
58+
for (VirtualFile vf:viewVfs) {
59+
for (VirtualFile file : VfsUtil.getAllSubFiles(vf)) {
60+
String label = file.getPath().replace(vf.getPath(), "");
61+
62+
pattern = label.matches("(/[\\w-]+){2}/template/.*")
63+
//e.g. (adminhtml|frontend|base|*)/(web|layout|*)/template/{whatever client typed}
64+
? Pattern.compile("^((/[\\w-]+){3}/)" + filePath)
65+
//e.g. (adminhtml|frontend|base|*)/(web|layout|*)/{whatever client typed}
66+
: Pattern.compile("^((/[\\w-]+){2}/)" + filePath);
67+
68+
matcher = pattern.matcher(label);
69+
if (!matcher.find()) {
70+
continue;
71+
}
72+
73+
//remove prefix
74+
label = label.replace(matcher.group(0), "");
75+
boolean lastPathSegment = !(label.indexOf("/", 1) > 0);
76+
label = lastPathSegment ? label : label.substring(0, label.indexOf("/", 1));
77+
label = filePathPrefix + filePath + label;
78+
label = !removeFileExtension ? label : (label.lastIndexOf(".") > 0
79+
? label.substring(0, label.lastIndexOf("."))
80+
: label
81+
);
82+
83+
result.addElement(
84+
LookupElementBuilder
85+
.create(label)
86+
.withIcon(lastPathSegment ? file.getFileType().getIcon() : AllIcons.Nodes.Folder)
87+
);
88+
}
89+
}
90+
}
91+
92+
private Collection<VirtualFile> findViewVfs(CompletionParameters parameters, CompletionResultSet result)
93+
{
94+
Collection<VirtualFile> viewVfs = new ArrayList<>();
95+
96+
Pattern pattern = Pattern.compile(RegExUtil.Magento.MODULE_NAME);
97+
Matcher matcher = pattern.matcher(result.getPrefixMatcher().getPrefix());
98+
99+
if (matcher.find()) {
100+
viewVfs.addAll(
101+
FileBasedIndexUtil.findViewVfsByModuleName(matcher.group(0), parameters.getPosition().getProject())
102+
);
103+
} else {
104+
VirtualFile moduleVf =
105+
VfsUtil.findVfUp(parameters.getOriginalFile().getVirtualFile(), "registration.php");
106+
107+
viewVfs.addAll(
108+
FileBasedIndexUtil.findViewVfsByModuleVf(moduleVf, parameters.getPosition().getProject())
109+
);
110+
}
111+
112+
return viewVfs;
113+
}
114+
}

src/com/magento/idea/magento2plugin/completion/xml/provider/LayoutBlockCompletionContributor.java src/com/magento/idea/magento2plugin/completion/provider/LayoutBlockCompletionContributor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;

src/com/magento/idea/magento2plugin/completion/xml/provider/LayoutContainerCompletionContributor.java src/com/magento/idea/magento2plugin/completion/provider/LayoutContainerCompletionContributor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;

src/com/magento/idea/magento2plugin/completion/xml/provider/LayoutUpdateCompletionContributor.java src/com/magento/idea/magento2plugin/completion/provider/LayoutUpdateCompletionContributor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.magento.idea.magento2plugin.completion.provider;
2+
3+
import com.intellij.codeInsight.completion.CompletionParameters;
4+
import com.intellij.codeInsight.completion.CompletionProvider;
5+
import com.intellij.codeInsight.completion.CompletionResultSet;
6+
import com.intellij.codeInsight.lookup.LookupElementBuilder;
7+
import com.intellij.icons.AllIcons;
8+
import com.intellij.psi.PsiElement;
9+
import com.intellij.util.ProcessingContext;
10+
import com.intellij.util.indexing.FileBasedIndex;
11+
import com.magento.idea.magento2plugin.stubs.indexes.ModuleNameIndex;
12+
import org.jetbrains.annotations.NotNull;
13+
14+
import java.util.Collection;
15+
16+
public class ModuleNameCompletionProvider extends CompletionProvider<CompletionParameters> {
17+
18+
@Override
19+
protected void addCompletions(@NotNull CompletionParameters parameters,
20+
ProcessingContext context,
21+
@NotNull CompletionResultSet result) {
22+
PsiElement position = parameters.getPosition().getOriginalElement();
23+
if (position == null) {
24+
return;
25+
}
26+
String prefix = result.getPrefixMatcher().getPrefix();
27+
28+
Collection<String> moduleNames
29+
= FileBasedIndex.getInstance().getAllKeys(ModuleNameIndex.KEY, position.getProject());
30+
31+
32+
moduleNames.removeIf(m -> !m.startsWith(prefix));
33+
for (String moduleName : moduleNames) {
34+
result.addElement(
35+
LookupElementBuilder
36+
.create(moduleName)
37+
.withIcon(AllIcons.Modules.ModulesNode)
38+
);
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package com.magento.idea.magento2plugin.completion.provider;
2+
3+
import com.intellij.codeInsight.completion.CompletionParameters;
4+
import com.intellij.codeInsight.completion.CompletionProvider;
5+
import com.intellij.codeInsight.completion.CompletionResultSet;
6+
import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
7+
import com.intellij.codeInsight.lookup.LookupElementBuilder;
8+
import com.intellij.psi.PsiElement;
9+
import com.intellij.psi.util.PsiTreeUtil;
10+
import com.intellij.util.ProcessingContext;
11+
import com.jetbrains.php.PhpIcons;
12+
import com.jetbrains.php.PhpIndex;
13+
import com.jetbrains.php.lang.psi.elements.PhpClass;
14+
import com.jetbrains.php.lang.psi.elements.PhpNamespace;
15+
import com.magento.idea.magento2plugin.php.util.PhpRegex;
16+
import gnu.trove.THashSet;
17+
import org.jetbrains.annotations.NotNull;
18+
19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.regex.Matcher;
22+
import java.util.regex.Pattern;
23+
import java.util.stream.Collectors;
24+
25+
public class PhpClassCompletionProvider extends CompletionProvider<CompletionParameters> {
26+
27+
final private static String PHP_CLASS_COMPLETION_REGEX
28+
= "\\\\?" + PhpRegex.CLASS_NAME + "\\\\?";
29+
30+
@Override
31+
protected void addCompletions(@NotNull CompletionParameters parameters,
32+
ProcessingContext context,
33+
@NotNull CompletionResultSet result) {
34+
PsiElement position = parameters.getPosition().getOriginalElement();
35+
if (position == null) {
36+
return;
37+
}
38+
String prefix = result.getPrefixMatcher().getPrefix();
39+
Matcher matcher = Pattern.compile(PHP_CLASS_COMPLETION_REGEX).matcher(prefix);
40+
if (!matcher.matches()) {
41+
return;
42+
}
43+
44+
String className = prefix.lastIndexOf(92) < 0 ? prefix : prefix.substring(prefix.lastIndexOf(92) + 1);
45+
String namespace = prefix.lastIndexOf(92) < 0 ? "" : prefix.substring(0, prefix.lastIndexOf(92));
46+
47+
PhpIndex phpIndex = PhpIndex.getInstance(parameters.getPosition().getProject());
48+
49+
final Collection<PhpClass> phpClasses = new THashSet<>();
50+
Collection<String> namespaceNames = new ArrayList<>();
51+
52+
if (!className.isEmpty()) {
53+
// case for input: "SomeClassOrNamespace"
54+
55+
// add classes
56+
Collection<String> classNames = phpIndex.getAllClassNames(new CamelHumpMatcher(className));
57+
for (String cName: classNames) {
58+
phpClasses.addAll(phpIndex.getClassesByName(cName));
59+
}
60+
// add interfaces
61+
Collection<String> interfaceNames = phpIndex.getAllInterfaceNames();
62+
interfaceNames.removeIf(i -> !i.contains(className));
63+
for (String iName: interfaceNames) {
64+
phpClasses.addAll(phpIndex.getInterfacesByName(iName));
65+
}
66+
if (!namespace.isEmpty()) {
67+
phpClasses.removeIf(c -> !c.getPresentableFQN().startsWith(namespace));
68+
} else {
69+
namespaceNames = phpIndex.getChildNamespacesByParentName("\\");
70+
namespaceNames.removeIf(n -> !n.contains(prefix));
71+
}
72+
} else {
73+
// case for input: "Some\Namespace\ + ^+<Space>"
74+
75+
// add namespaces
76+
Collection<PhpNamespace> namespaces = phpIndex.getNamespacesByName(("\\" + namespace).toLowerCase());
77+
for (PhpNamespace nsp: namespaces) {
78+
phpClasses.addAll(PsiTreeUtil.getChildrenOfTypeAsList(nsp.getStatements(), PhpClass.class));
79+
}
80+
81+
// add namespaces and classes (string representation)
82+
namespaceNames
83+
= phpIndex.getChildNamespacesByParentName("\\".concat(namespace).concat("\\").toLowerCase());
84+
namespaceNames
85+
= namespaceNames.stream().map(n -> namespace.concat("\\").concat(n)).collect(Collectors.toList());
86+
}
87+
88+
// add all above founded items to lookup builder
89+
// order is important (items with the same name override each other), add classes first
90+
for (PhpClass phpClass : phpClasses) {
91+
result.addElement(
92+
LookupElementBuilder
93+
.create(phpClass.getPresentableFQN())
94+
.withIcon(phpClass.getIcon())
95+
);
96+
}
97+
98+
for (String nsName : namespaceNames) {
99+
result.addElement(
100+
LookupElementBuilder
101+
.create(nsName)
102+
.withIcon(PhpIcons.NAMESPACE)
103+
);
104+
}
105+
}
106+
}

src/com/magento/idea/magento2plugin/completion/xml/provider/PhpClassMemberCompletionProvider.java src/com/magento/idea/magento2plugin/completion/provider/PhpClassMemberCompletionProvider.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;

src/com/magento/idea/magento2plugin/completion/xml/provider/PhpConstructorArgumentCompletionProvider.java src/com/magento/idea/magento2plugin/completion/provider/PhpConstructorArgumentCompletionProvider.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;

src/com/magento/idea/magento2plugin/completion/xml/provider/PhpServiceMethodCompletionContributor.java src/com/magento/idea/magento2plugin/completion/provider/PhpServiceMethodCompletionContributor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;

src/com/magento/idea/magento2plugin/completion/xml/provider/VirtualTypeCompletionProvider.java src/com/magento/idea/magento2plugin/completion/provider/VirtualTypeCompletionProvider.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.magento.idea.magento2plugin.completion.xml.provider;
1+
package com.magento.idea.magento2plugin.completion.provider;
22

33
import com.intellij.codeInsight.completion.CompletionParameters;
44
import com.intellij.codeInsight.completion.CompletionProvider;

src/com/magento/idea/magento2plugin/completion/xml/XmlCompletionContributor.java

+9-8
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
import com.intellij.codeInsight.completion.CompletionType;
55
import com.intellij.patterns.XmlPatterns;
66
import com.intellij.psi.xml.XmlTokenType;
7-
import com.magento.idea.magento2plugin.completion.xml.provider.*;
8-
import com.magento.idea.magento2plugin.php.util.PhpRegex;
7+
import com.magento.idea.magento2plugin.completion.provider.*;
98

109
import static com.intellij.patterns.PlatformPatterns.psiElement;
1110
import static com.intellij.patterns.StandardPatterns.string;
@@ -14,19 +13,21 @@
1413
public class XmlCompletionContributor extends CompletionContributor {
1514

1615
public XmlCompletionContributor() {
17-
extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN)
18-
.withText(string().matches(PhpRegex.Xml.CLASS_ELEMENT)),
16+
extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN),
1917
new CompositeCompletionProvider(
2018
new PhpClassCompletionProvider(),
21-
new PhpClassMemberCompletionProvider()
19+
new PhpClassMemberCompletionProvider(),
20+
new ModuleNameCompletionProvider(),
21+
new FilePathCompletionProvider()
2222
)
2323
);
2424

25-
extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_DATA_CHARACTERS)
26-
.withText(string().matches(PhpRegex.Xml.CLASS_ELEMENT)),
25+
extend(CompletionType.BASIC, psiElement(XmlTokenType.XML_DATA_CHARACTERS),
2726
new CompositeCompletionProvider(
2827
new PhpClassCompletionProvider(),
29-
new PhpClassMemberCompletionProvider()
28+
new PhpClassMemberCompletionProvider(),
29+
new ModuleNameCompletionProvider(),
30+
new FilePathCompletionProvider()
3031
)
3132
);
3233

0 commit comments

Comments
 (0)