From fd11bd29ddd945fe69e8697bb4c0fe7c926fa70c Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 22 Nov 2013 20:55:02 +0400 Subject: [PATCH] Support for temporary and configuration-based IntelliLang injections in Python (PY-10983, PY-10721) --- .../META-INF/intellilang-python-support.xml | 2 + .../intelliLang/PyConfigurationInjector.java | 33 +++++++++++++++++ .../PyLanguageInjectionSupport.java | 35 ------------------ .../intelliLang/PyTemporaryInjector.java | 30 +++++++++++++++ .../python/codeInsight/PyInjectionUtil.java | 18 ++++++--- .../python/codeInsight/PyInjectorBase.java | 37 +++++++++++++++++++ .../regexp/PythonRegexpInjector.java | 2 +- 7 files changed, 116 insertions(+), 41 deletions(-) create mode 100644 python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyConfigurationInjector.java create mode 100644 python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyTemporaryInjector.java create mode 100644 python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java diff --git a/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml b/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml index 2ffa9f7c2325..624d146a0378 100644 --- a/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml +++ b/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml @@ -7,5 +7,7 @@ + + \ No newline at end of file diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyConfigurationInjector.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyConfigurationInjector.java new file mode 100644 index 000000000000..f672f5d17ee6 --- /dev/null +++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyConfigurationInjector.java @@ -0,0 +1,33 @@ +package com.jetbrains.python.intelliLang; + +import com.intellij.lang.Language; +import com.intellij.psi.PsiElement; +import com.jetbrains.python.codeInsight.PyInjectorBase; +import org.intellij.plugins.intelliLang.Configuration; +import org.intellij.plugins.intelliLang.inject.InjectedLanguage; +import org.intellij.plugins.intelliLang.inject.InjectorUtils; +import org.intellij.plugins.intelliLang.inject.LanguageInjectionSupport; +import org.intellij.plugins.intelliLang.inject.config.BaseInjection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author vlan + */ +public class PyConfigurationInjector extends PyInjectorBase { + @Nullable + @Override + public Language getInjectedLanguage(@NotNull PsiElement context) { + for (LanguageInjectionSupport support : InjectorUtils.getActiveInjectionSupports()) { + if (support instanceof PyLanguageInjectionSupport) { + final Configuration configuration = Configuration.getInstance(); + for (BaseInjection injection : configuration.getInjections(support.getId())) { + if (injection.acceptsPsiElement(context)) { + return InjectedLanguage.findLanguageById(injection.getInjectedLanguageId()); + } + } + } + } + return null; + } +} diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java index 5a91db5ce994..43db1f20d814 100644 --- a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java +++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java @@ -15,22 +15,14 @@ */ package com.jetbrains.python.intelliLang; -import com.intellij.lang.Language; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; import com.intellij.psi.PsiLanguageInjectionHost; import com.jetbrains.python.patterns.PythonPatterns; import com.jetbrains.python.psi.PyElement; -import com.jetbrains.python.psi.PyStringLiteralExpression; import org.intellij.plugins.intelliLang.inject.AbstractLanguageInjectionSupport; -import org.intellij.plugins.intelliLang.inject.config.BaseInjection; -import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; - /** * @author yole */ @@ -54,33 +46,6 @@ public class PyLanguageInjectionSupport extends AbstractLanguageInjectionSupport return host instanceof PyElement; } - @Override - public boolean useDefaultInjector(PsiLanguageInjectionHost host) { - return true; - } - - @Override - public BaseInjection createInjection(Element element) { - // This is how DefaultLanguageInjector gets its injection ranges - return new BaseInjection(getId()) { - @NotNull - @Override - public List getInjectedArea(PsiElement element) { - if (element instanceof PyStringLiteralExpression) { - return ((PyStringLiteralExpression)element).getStringValueTextRanges(); - } - return super.getInjectedArea(element); - } - }; - } - - @Override - public boolean addInjectionInPlace(Language language, PsiLanguageInjectionHost psiElement) { - // XXX: Disable temporary injections via intention actions for Python elements, since TemporaryPlacesInjector cannot handle elements - // with multiple injection text ranges (PY-10691) - return true; - } - @Nullable @Override public String getHelpId() { diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyTemporaryInjector.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyTemporaryInjector.java new file mode 100644 index 000000000000..d88905d623cf --- /dev/null +++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyTemporaryInjector.java @@ -0,0 +1,30 @@ +package com.jetbrains.python.intelliLang; + +import com.intellij.lang.Language; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiLanguageInjectionHost; +import com.jetbrains.python.codeInsight.PyInjectorBase; +import org.intellij.plugins.intelliLang.inject.InjectedLanguage; +import org.intellij.plugins.intelliLang.inject.TemporaryPlacesRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author vlan + */ +public class PyTemporaryInjector extends PyInjectorBase { + @Nullable + @Override + public Language getInjectedLanguage(@NotNull PsiElement context) { + final TemporaryPlacesRegistry registry = TemporaryPlacesRegistry.getInstance(context.getProject()); + if (context instanceof PsiLanguageInjectionHost) { + final PsiFile file = context.getContainingFile(); + final InjectedLanguage injectedLanguage = registry.getLanguageFor((PsiLanguageInjectionHost)context, file); + if (injectedLanguage != null) { + return injectedLanguage.getLanguage(); + } + } + return null; + } +} diff --git a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java index d68e6c61c47f..d0a49fe810d7 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java @@ -23,6 +23,7 @@ import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; import java.util.List; import static com.jetbrains.python.inspections.PyStringFormatParser.*; @@ -31,15 +32,22 @@ import static com.jetbrains.python.inspections.PyStringFormatParser.*; * @author vlan */ public class PyInjectionUtil { + public static final List> ELEMENTS_TO_INJECT_IN = + Arrays.asList(PyStringLiteralExpression.class, PyParenthesizedExpression.class, PyBinaryExpression.class, PyCallExpression.class); + private PyInjectionUtil() {} /** - * Returns true if the element is the largest expression that represents a string literal, possibly with concatenation, parentheses, - * or formatting. + * Returns the largest expression in the specified context that represents a string literal suitable for language injection, possibly + * with concatenation, parentheses, or formatting. */ - public static boolean isLargestStringLiteral(@NotNull PsiElement element) { - final PsiElement parent = element.getParent(); - return isStringLiteralPart(element) && (parent == null || !isStringLiteralPart(parent)); + @Nullable + public static PsiElement getLargestStringLiteral(@NotNull PsiElement context) { + PsiElement element = null; + for (PsiElement current = context; current != null && isStringLiteralPart(current); current = current.getParent()) { + element = current; + } + return element; } /** diff --git a/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java b/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java new file mode 100644 index 000000000000..48b62368af3d --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/PyInjectorBase.java @@ -0,0 +1,37 @@ +package com.jetbrains.python.codeInsight; + +import com.intellij.lang.Language; +import com.intellij.lang.injection.MultiHostInjector; +import com.intellij.lang.injection.MultiHostRegistrar; +import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * @author vlan + */ +public abstract class PyInjectorBase implements MultiHostInjector { + @Override + public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { + final Language language = getInjectedLanguage(context); + if (language != null) { + final PsiElement element = PyInjectionUtil.getLargestStringLiteral(context); + if (element != null) { + registrar.startInjecting(language); + PyInjectionUtil.registerStringLiteralInjection(element, registrar); + registrar.doneInjecting(); + } + } + } + + @NotNull + @Override + public List> elementsToInjectIn() { + return PyInjectionUtil.ELEMENTS_TO_INJECT_IN; + } + + @Nullable + public abstract Language getInjectedLanguage(@NotNull PsiElement context); +} diff --git a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java index 39c9aff38410..bb23c475a576 100644 --- a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java +++ b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java @@ -67,7 +67,7 @@ public class PythonRegexpInjector implements MultiHostInjector { @Override public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { final PsiElement contextParent = context.getParent(); - if (PyInjectionUtil.isLargestStringLiteral(context) && contextParent instanceof PyArgumentList) { + if (PyInjectionUtil.getLargestStringLiteral(context) == context && contextParent instanceof PyArgumentList) { final PyExpression[] args = ((PyArgumentList)contextParent).getArguments(); int index = ArrayUtil.indexOf(args, context); PyCallExpression call = PsiTreeUtil.getParentOfType(context, PyCallExpression.class);