package org.angularjs.editor;
import com.intellij.lang.Language;
import com.intellij.lang.css.CSSLanguage;
import com.intellij.lang.injection.MultiHostInjector;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.lang.javascript.inject.JSFormattableInjectionUtil;
import com.intellij.lang.javascript.psi.*;
import com.intellij.lang.javascript.psi.impl.JSLiteralExpressionImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.ElementManipulators;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLanguageInjectionHost;
import com.intellij.psi.impl.source.xml.XmlAttributeValueImpl;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.util.ObjectUtils;
import org.angularjs.codeInsight.attributes.AngularAttributesRegistry;
import org.angularjs.html.Angular2HTMLLanguage;
import org.angularjs.index.AngularIndexUtil;
import org.angularjs.index.AngularJS2IndexingHandler;
import org.angularjs.lang.AngularJSLanguage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
/**
* @author Dennis.Ushakov
*/
public class Angular2Injector implements MultiHostInjector {
@Override
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
final Project project = context.getProject();
if (!AngularIndexUtil.hasAngularJS2(project)) return;
final PsiElement parent = context.getParent();
if (context instanceof JSLiteralExpressionImpl && ((JSLiteralExpressionImpl)context).isQuotedLiteral()) {
if (injectIntoDirectiveProperty(registrar, context, parent, "template", Angular2HTMLLanguage.INSTANCE)) return;
if (injectIntoEmbeddedLiteral(registrar, context, parent)) return;
if (parent instanceof JSArrayLiteralExpression) {
final JSProperty property = ObjectUtils.tryCast(parent.getParent(), JSProperty.class);
if (injectIntoDirectiveProperty(registrar, context, property, "styles", CSSLanguage.INSTANCE)) return;
}
if (parent instanceof JSProperty && parent.getParent() instanceof JSObjectLiteralExpression) {
injectIntoDirectiveProperty(registrar, context, parent.getParent().getParent(), "host", AngularJSLanguage.INSTANCE);
}
return;
}
if (context instanceof XmlAttributeValueImpl && parent instanceof XmlAttribute) {
final int length = context.getTextLength();
final String name = ((XmlAttribute)parent).getName();
if (isInjectableAttribute(project, length, name)) {
registrar.startInjecting(AngularJSLanguage.INSTANCE).
addPlace(null, null, (PsiLanguageInjectionHost)context, ElementManipulators.getValueTextRange(context)).
doneInjecting();
}
}
}
private boolean injectIntoEmbeddedLiteral(MultiHostRegistrar registrar, PsiElement context, PsiElement parent) {
if (parent instanceof JSEmbeddedContent) {
final XmlAttribute attribute = PsiTreeUtil.getParentOfType(parent, XmlAttribute.class);
if (attribute != null && isInjectableAttribute(context.getProject(), context.getTextLength(), attribute.getName())) {
final TextRange range = ElementManipulators.getValueTextRange(context);
registrar.startInjecting(AngularJSLanguage.INSTANCE).addPlace(null, null, (PsiLanguageInjectionHost)context, range).doneInjecting();
return true;
}
}
return false;
}
protected boolean isInjectableAttribute(Project project, int valueLength, String name) {
return (AngularAttributesRegistry.isEventAttribute(name, project) ||
AngularAttributesRegistry.isBindingAttribute(name, project) ||
AngularAttributesRegistry.isTemplateAttribute(name, project)) &&
valueLength > 0;
}
protected boolean injectIntoDirectiveProperty(@NotNull MultiHostRegistrar registrar,
@NotNull PsiElement context,
@Nullable PsiElement parent,
@NotNull String name,
@NotNull Language language) {
if (parent instanceof JSProperty && name.equals(((JSProperty)parent).getName())) {
final JSCallExpression callExpression = PsiTreeUtil.getParentOfType(parent, JSCallExpression.class);
final JSExpression expression = callExpression != null ? callExpression.getMethodExpression() : null;
if (expression instanceof JSReferenceExpression) {
final String command = ((JSReferenceExpression)expression).getReferenceName();
if (!AngularJS2IndexingHandler.isDirective(command) && !"View".equals(command)) return true;
final TextRange range = ElementManipulators.getValueTextRange(context);
registrar.startInjecting(language).addPlace(null, null, (PsiLanguageInjectionHost)context, range).doneInjecting();
JSFormattableInjectionUtil.setReformattableInjection(registrar, context);
}
}
return false;
}
@NotNull
@Override
public List<Class<? extends PsiElement>> elementsToInjectIn() {
return Arrays.asList(JSLiteralExpression.class, XmlAttributeValue.class);
}
}