package fr.adrienbrault.idea.symfony2plugin.dic;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementWeigher;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.ServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.XmlCallServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.XmlConstructServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.YamlConstructServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class ServiceCompletionProvider extends CompletionProvider<CompletionParameters> {
private static ServiceSuggestionCollector[] COLLECTORS = new ServiceSuggestionCollector[] {
new XmlConstructServiceSuggestionCollector(),
new YamlConstructServiceSuggestionCollector(),
new XmlCallServiceSuggestionCollector(),
};
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet resultSet) {
if(!Symfony2ProjectComponent.isEnabled(parameters.getPosition())) {
return;
}
PsiElement element = parameters.getPosition();
PrioritizedLookupResult result = getLookupElements(
element, ContainerCollectionResolver.getServices(element.getProject()).values()
);
addPrioritizedServiceLookupElements(parameters, resultSet, result);
}
public static void addPrioritizedServiceLookupElements(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet resultSet, @NotNull PrioritizedLookupResult result) {
// move known elements to top
if(result.getTopStrings().size() > 0) {
CompletionSorter completionSorter = CompletionService.getCompletionService()
.defaultSorter(parameters, resultSet.getPrefixMatcher())
.weighBefore("priority", new MyLookupElementWeigher(result.getTopStrings()));
resultSet = resultSet.withRelevanceSorter(completionSorter);
}
resultSet.addAllElements(result.getLookupElements());
}
@NotNull
public static PrioritizedLookupResult getLookupElements(@Nullable PsiElement element, @NotNull Collection<ContainerService> services) {
// collect instance to highlight services
List<String> servicesForInstance = new ArrayList<>();
if(element != null) {
Set<String> servicesForInstanceSet = new java.util.HashSet<>();
for (ServiceSuggestionCollector collector : COLLECTORS) {
servicesForInstanceSet.addAll(collector.collect(element, services));
}
servicesForInstance.addAll(
ServiceContainerUtil.getSortedServiceId(element.getProject(), servicesForInstanceSet)
);
}
Collection<LookupElement> collect = services.stream()
.map((Function<ContainerService, LookupElement>)
service -> new ServiceStringLookupElement(service, servicesForInstance.contains(service.getName())))
.collect(Collectors.toList());
return new PrioritizedLookupResult(collect, servicesForInstance);
}
public static class PrioritizedLookupResult {
@NotNull
private final Collection<LookupElement> lookupElements;
@NotNull
private final List<String> topStrings;
public PrioritizedLookupResult(@NotNull Collection<LookupElement> lookupElements, @NotNull List<String> topStrings) {
this.lookupElements = lookupElements;
this.topStrings = topStrings;
}
@NotNull
public Collection<LookupElement> getLookupElements() {
return lookupElements;
}
@NotNull
public List<String> getTopStrings() {
return topStrings;
}
}
private static class MyLookupElementWeigher extends LookupElementWeigher {
@NotNull
private final List<String> elements;
MyLookupElementWeigher(@NotNull List<String> elements) {
super("topElement");
this.elements = elements;
// top most element ist first
Collections.reverse(elements);
}
@Nullable
@Override
public Comparable weigh(@NotNull LookupElement element) {
String lookupString = element.getLookupString();
if(!elements.contains(lookupString)) {
return 0;
}
// start by "0"
int pos = elements.indexOf(lookupString) + 1;
// we reversed the list so, top most element has higher negative value now
return -1000 - pos;
}
}
}