Skip to content

Commit 3872b6c

Browse files
committed
Class name autocompletion in the middle of the value in xml/yaml services #337
1 parent adc34d0 commit 3872b6c

File tree

4 files changed

+106
-3
lines changed

4 files changed

+106
-3
lines changed

src/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java

+64
Original file line numberDiff line numberDiff line change
@@ -745,4 +745,68 @@ protected boolean accept(PsiReference psiReference) {
745745

746746
}
747747

748+
/**
749+
* Try to visit possible class name for PsiElements with text like "Foo\|Bar", "Foo|\Bar", "\Foo|\Bar"
750+
* Cursor must have position in PsiElement
751+
*
752+
* @param psiElement the element context, cursor should be in it
753+
* @param cursorOffset current cursor editor eg from completion context
754+
* @param visitor callback on matching class
755+
*/
756+
public static void visitNamespaceClassForCompletion(PsiElement psiElement, int cursorOffset, ClassForCompletionVisitor visitor) {
757+
758+
int cursorOffsetClean = cursorOffset - psiElement.getTextOffset();
759+
if(cursorOffsetClean < 1) {
760+
return;
761+
}
762+
763+
String content = psiElement.getText();
764+
int length = content.length();
765+
if(!(length >= cursorOffsetClean)) {
766+
return;
767+
}
768+
769+
String beforeCursor = content.substring(0, cursorOffsetClean);
770+
boolean isValid;
771+
772+
// espend\|Container, espend\Cont|ainer <- fallback to last full namespace
773+
// espend|\Container <- only on known namespace "espend"
774+
String namespace = beforeCursor;
775+
776+
// if no backslash or its equal in first position, fallback on namespace completion
777+
int lastSlash = beforeCursor.lastIndexOf("\\");
778+
if(lastSlash <= 0) {
779+
isValid = PhpIndexUtil.hasNamespace(psiElement.getProject(), beforeCursor);
780+
} else {
781+
isValid = true;
782+
namespace = beforeCursor.substring(0, lastSlash);
783+
}
784+
785+
if(!isValid) {
786+
return;
787+
}
788+
789+
// format namespaces and add prefix for fluent completion
790+
String prefix = "";
791+
if(namespace.startsWith("\\")) {
792+
prefix = "\\";
793+
} else {
794+
namespace = "\\" + namespace;
795+
}
796+
797+
// search classes in current namespace and child namespaces
798+
for(PhpClass phpClass: PhpIndexUtil.getPhpClassInsideNamespace(psiElement.getProject(), namespace)) {
799+
String presentableFQN = phpClass.getPresentableFQN();
800+
if(presentableFQN != null && fr.adrienbrault.idea.symfony2plugin.util.StringUtils.startWithEqualClassname(presentableFQN, beforeCursor)) {
801+
visitor.visit(phpClass, presentableFQN, prefix);
802+
}
803+
804+
}
805+
806+
}
807+
808+
public static interface ClassForCompletionVisitor {
809+
public void visit(PhpClass phpClass, String presentableFQN, String prefix);
810+
}
811+
748812
}

src/fr/adrienbrault/idea/symfony2plugin/util/PhpIndexUtil.java

+10
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,14 @@ public boolean process(PhpNamespace phpNamespace) {
4848
return phpClasses;
4949
}
5050

51+
public static boolean hasNamespace(Project project, String namespaceName) {
52+
53+
if(!namespaceName.startsWith("\\")) {
54+
namespaceName = "\\" + namespaceName;
55+
}
56+
57+
return PhpIndex.getInstance(project).getChildNamespacesByParentName(namespaceName + "\\").size() > 0;
58+
59+
}
60+
5161
}

src/fr/adrienbrault/idea/symfony2plugin/util/StringUtils.java

+19
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,23 @@ public static String ucfirst(String chaine){
3333
return chaine.substring(0, 1).toUpperCase() + chaine.substring(1);
3434
}
3535

36+
/**
37+
* Simple string compare if class name is in same namespace
38+
* Starting backslash doesnt break equal check
39+
*
40+
* @param class1 \Foo\Class
41+
* @param class2 \Foo or Foo
42+
*/
43+
public static boolean startWithEqualClassname(String class1, String class2) {
44+
if(class1.startsWith("\\")) {
45+
class1 = class1.substring(1);
46+
}
47+
48+
if(class2.startsWith("\\")) {
49+
class2 = class2.substring(1);
50+
}
51+
52+
return class1.startsWith(class2);
53+
}
54+
3655
}

src/fr/adrienbrault/idea/symfony2plugin/util/completion/PhpClassAndParameterCompletionProvider.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,42 @@
44
import com.intellij.codeInsight.completion.CompletionParameters;
55
import com.intellij.codeInsight.completion.CompletionProvider;
66
import com.intellij.codeInsight.completion.CompletionResultSet;
7+
import com.intellij.codeInsight.lookup.LookupElementBuilder;
78
import com.intellij.psi.PsiElement;
89
import com.intellij.util.ProcessingContext;
910
import com.jetbrains.php.PhpIndex;
1011
import com.jetbrains.php.completion.PhpLookupElement;
12+
import com.jetbrains.php.lang.psi.elements.PhpClass;
1113
import com.jetbrains.php.lang.psi.stubs.indexes.PhpClassIndex;
1214
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1315
import fr.adrienbrault.idea.symfony2plugin.config.component.ParameterLookupElement;
1416
import fr.adrienbrault.idea.symfony2plugin.config.yaml.ParameterPercentWrapInsertHandler;
1517
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerParameter;
1618
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
19+
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
1720
import org.jetbrains.annotations.NotNull;
1821

1922
import java.util.Map;
2023

2124
public class PhpClassAndParameterCompletionProvider extends CompletionProvider<CompletionParameters> {
2225

23-
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet resultSet) {
26+
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet resultSet) {
2427

2528
PsiElement psiElement = parameters.getOriginalPosition();
2629
if(psiElement == null || !Symfony2ProjectComponent.isEnabled(psiElement)) {
2730
return;
2831
}
2932

30-
PhpIndex phpIndex = PhpIndex.getInstance(psiElement.getProject());
33+
// Foo\|Bar
34+
// Foo|\Bar
35+
PhpElementsUtil.visitNamespaceClassForCompletion(psiElement, parameters.getOffset(), new PhpElementsUtil.ClassForCompletionVisitor() {
36+
@Override
37+
public void visit(PhpClass phpClass, String presentableFQN, String prefix) {
38+
resultSet.addElement(LookupElementBuilder.create(prefix + presentableFQN).withIcon(phpClass.getIcon()));
39+
}
40+
});
3141

32-
for (String className : phpIndex.getAllClassNames(resultSet.getPrefixMatcher())) {
42+
for (String className : PhpIndex.getInstance(psiElement.getProject()).getAllClassNames(resultSet.getPrefixMatcher())) {
3343
resultSet.addElement(new PhpLookupElement(className, PhpClassIndex.KEY, parameters.getOriginalFile().getProject(), PhpClassReferenceInsertHandler.getInstance()));
3444
}
3545

0 commit comments

Comments
 (0)