package org.angularjs.editor; import com.intellij.json.JsonLanguage; import com.intellij.lang.injection.MultiHostInjector; import com.intellij.lang.injection.MultiHostRegistrar; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.InjectorMatchingEndFinder; 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.impl.source.xml.XmlTextImpl; import com.intellij.psi.templateLanguages.OuterLanguageElement; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTokenType; import org.angularjs.codeInsight.attributes.AngularAttributesRegistry; import org.angularjs.index.AngularIndexUtil; import org.angularjs.lang.AngularJSLanguage; import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.List; /** * @author Dennis.Ushakov */ public class AngularJSInjector implements MultiHostInjector { private static final Logger LOG = Logger.getInstance(AngularJSInjector.class); @Override public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { // check that we have angular directives indexed before injecting final Project project = context.getProject(); if (!AngularIndexUtil.hasAngularJS(project)) return; final PsiElement parent = context.getParent(); if (context instanceof XmlAttributeValueImpl && parent instanceof XmlAttribute) { final String value = context.getText(); final int start = value.startsWith("'") || value.startsWith("\"") ? 1 : 0; final int end = value.endsWith("'") || value.endsWith("\"") ? 1 : 0; final int length = value.length(); if (AngularAttributesRegistry.isAngularExpressionAttribute((XmlAttribute)parent) && length > 1) { registrar.startInjecting(AngularJSLanguage.INSTANCE). addPlace(null, null, (PsiLanguageInjectionHost)context, new TextRange(start, length - end)). doneInjecting(); return; } if (AngularAttributesRegistry.isJSONAttribute((XmlAttribute)parent) && length > 1) { registrar.startInjecting(JsonLanguage.INSTANCE). addPlace(null, null, (PsiLanguageInjectionHost)context, new TextRange(start, length - end)). doneInjecting(); return; } } if (context instanceof XmlTextImpl || context instanceof XmlAttributeValueImpl) { final String start = AngularJSBracesUtil.getInjectionStart(project); final String end = AngularJSBracesUtil.getInjectionEnd(project); if (AngularJSBracesUtil.hasConflicts(start, end, context)) return; final String text = context.getText(); int endIndex = -1; int afterStart = -1; while (true) { final int startIdx = text.indexOf(start, Math.max(endIndex, afterStart)); afterStart = startIdx < 0 ? -1 : (startIdx + start.length()); if (afterStart < 0) return; endIndex = afterStart; endIndex = InjectorMatchingEndFinder.findMatchingEnd(start, end, text, endIndex); endIndex = endIndex > 0 ? endIndex : ElementManipulators.getValueTextRange(context).getEndOffset(); final PsiElement injectionCandidate = afterStart >= 0 ? context.findElementAt(afterStart) : null; if (injectionCandidate != null && injectionCandidate.getNode().getElementType() != XmlTokenType.XML_COMMENT_CHARACTERS && !(injectionCandidate instanceof OuterLanguageElement)) { if (afterStart > endIndex) { if (ApplicationManager.getApplication().isInternal()) { LOG.error("Braces: " + start + "," + end + "\n" + "Text: \"" + text + "\"" + "\n" + "Interval: (" + afterStart + "," + endIndex + ")" + "\n" + "File: " + context.getContainingFile().getName() + ", language:" + context.getContainingFile().getLanguage()); } return; } registrar.startInjecting(AngularJSLanguage.INSTANCE). addPlace(null, null, (PsiLanguageInjectionHost)context, new TextRange(afterStart, endIndex)). doneInjecting(); } } } } @NotNull @Override public List<? extends Class<? extends PsiElement>> elementsToInjectIn() { return Arrays.asList(XmlTextImpl.class, XmlAttributeValueImpl.class); } }