package fr.adrienbrault.idea.symfony2plugin.intentions.yaml;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
import fr.adrienbrault.idea.symfony2plugin.translation.util.TranslationInsertUtil;
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceTag;
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.yaml.YAMLFileType;
import org.jetbrains.yaml.YAMLUtil;
import org.jetbrains.yaml.psi.YAMLCompoundValue;
import org.jetbrains.yaml.psi.YAMLKeyValue;
import org.jetbrains.yaml.psi.YAMLSequence;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class YamlServiceTagIntention extends PsiElementBaseIntentionAction {
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) {
if(psiElement.getContainingFile().getFileType() != YAMLFileType.YML || !Symfony2ProjectComponent.isEnabled(psiElement.getProject())) {
return false;
}
return YamlHelper.findServiceInContext(psiElement) != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) throws IncorrectOperationException {
YAMLKeyValue serviceKeyValue = YamlHelper.findServiceInContext(psiElement);
if(serviceKeyValue == null) {
return;
}
Pair<PhpClass, Set<String>> invoke = invoke(project, serviceKeyValue);
if(invoke == null) {
return;
}
Set<String> phpClassServiceTags = invoke.getSecond();
if(phpClassServiceTags.size() == 0) {
HintManager.getInstance().showErrorHint(editor, "Ops, no possible Tag found");
return;
}
PhpClass resolvedClassDefinition = invoke.getFirst();
int appendEndOffset = -1;
String insertString = null;
YAMLKeyValue argumentsKeyValue = YamlHelper.getYamlKeyValue(serviceKeyValue, "tags");
if(argumentsKeyValue == null) {
// we dont an "tags" key so we need to create one
String indent = StringUtil.repeatSymbol(' ', YAMLUtil.getIndentToThisElement(serviceKeyValue));
List<String> yamlSequences = new ArrayList<>();
for (String item : phpClassServiceTags) {
ServiceTag serviceTag = new ServiceTag(resolvedClassDefinition, item);
ServiceUtil.decorateServiceTag(serviceTag);
yamlSequences.add(indent + " " + serviceTag.toYamlString());
}
appendEndOffset = serviceKeyValue.getTextRange().getEndOffset();
String eol = TranslationInsertUtil.findEol(serviceKeyValue);
insertString = eol + indent + "tags:" + eol + StringUtils.join(yamlSequences, eol);
} else {
// we found a "tags" key so update
PsiElement value = argumentsKeyValue.getValue();
if(!(value instanceof YAMLCompoundValue)) {
HintManager.getInstance().showErrorHint(editor, "Sry, not supported tags definition");
return;
}
PsiElement firstChild = value.getFirstChild();
if(firstChild instanceof YAMLSequence) {
String indent = StringUtil.repeatSymbol(' ', YAMLUtil.getIndentToThisElement(argumentsKeyValue));
List<String> yamlSequences = new ArrayList<>();
for (String item : phpClassServiceTags) {
ServiceTag serviceTag = new ServiceTag(resolvedClassDefinition, item);
ServiceUtil.decorateServiceTag(serviceTag);
yamlSequences.add(indent + serviceTag.toYamlString());
}
appendEndOffset = argumentsKeyValue.getTextRange().getEndOffset();
String eol = TranslationInsertUtil.findEol(argumentsKeyValue);
insertString = eol + StringUtils.join(yamlSequences, eol);
}
}
if(appendEndOffset == -1) {
HintManager.getInstance().showErrorHint(editor, "Sry, not supported service definition");
return;
}
PsiDocumentManager manager = PsiDocumentManager.getInstance(project);
Document document = manager.getDocument(serviceKeyValue.getContainingFile());
if (document == null) {
return;
}
document.insertString(appendEndOffset, insertString);
manager.doPostponedOperationsAndUnblockDocument(document);
manager.commitDocument(document);
}
@NotNull
@Override
public String getFamilyName() {
return "Symfony";
}
@NotNull
@Override
public String getText() {
return "Symfony: Add Tags";
}
@Nullable
private static Pair<PhpClass, Set<String>> invoke(@NotNull Project project, @NotNull YAMLKeyValue serviceKeyValue) {
String aClass = YamlHelper.getYamlKeyValueAsString(serviceKeyValue, "class");
if(aClass == null|| StringUtils.isBlank(aClass)) {
return null;
}
PhpClass resolvedClassDefinition = ServiceUtil.getResolvedClassDefinition(project, aClass, new ContainerCollectionResolver.LazyServiceCollector(project));
if(resolvedClassDefinition == null) {
return null;
}
Set<String> phpClassServiceTags = ServiceUtil.getPhpClassServiceTags(resolvedClassDefinition);
Set<String> strings = YamlHelper.collectServiceTags(serviceKeyValue);
if(strings != null && strings.size() > 0) {
for (String s : strings) {
if(phpClassServiceTags.contains(s)) {
phpClassServiceTags.remove(s);
}
}
}
return Pair.create(resolvedClassDefinition, phpClassServiceTags);
}
}