Merge branch 'python-intellilang-injector'

This commit is contained in:
Andrey Vlasovskikh
2013-11-22 21:41:43 +04:00
8 changed files with 137 additions and 46 deletions
@@ -7,5 +7,7 @@
</extensions>
<extensions defaultExtensionNs="com.intellij">
<patterns.patternClass className="com.jetbrains.python.patterns.PythonPatterns" alias="py"/>
<multiHostInjector implementation="com.jetbrains.python.intelliLang.PyTemporaryInjector"/>
<multiHostInjector implementation="com.jetbrains.python.intelliLang.PyConfigurationInjector"/>
</extensions>
</idea-plugin>
@@ -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;
}
}
@@ -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<TextRange> 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() {
@@ -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;
}
}
@@ -9,6 +9,6 @@
</injection>
<injection language="SQL" injector-id="python">
<display-name>"SQL select/delete/insert/update/create"</display-name>
<place><![CDATA[pyLiteralExpression().withText(string().matchesBrics(" *(((SELECT|DELETE) .*FROM)|((INSERT|REPLACE) .*INTO)|(UPDATE .* SET)|((CREATE|DROP|ALTER) +(TABLE|INDEX))) .*"))]]></place>
<place><![CDATA[pyLiteralExpression().withText(string().matchesBrics(".*(((SELECT|DELETE) .*FROM)|((INSERT|REPLACE) .*INTO)|(UPDATE .* SET)|((CREATE|DROP|ALTER) +(TABLE|INDEX))) .*"))]]></place>
</injection>
</component>
@@ -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<Class<? extends PyExpression>> 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;
}
/**
@@ -47,7 +55,7 @@ public class PyInjectionUtil {
* string concatenations or formatting.
*/
public static void registerStringLiteralInjection(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) {
processStringLiteral(element, registrar, "", "", Formatting.NONE);
processStringLiteral(element, registrar, "", "", Formatting.PERCENT);
}
private static boolean isStringLiteralPart(@NotNull PsiElement element) {
@@ -100,9 +108,25 @@ public class PyInjectionUtil {
final FormatStringChunk chunk = chunks.get(i);
if (chunk instanceof ConstantChunk) {
final int nextIndex = i + 1;
final String chunkPrefix = i == 1 && chunks.get(0) instanceof SubstitutionChunk ? missingValue : "";
final String chunkSuffix = nextIndex < chunks.size() &&
chunks.get(nextIndex) instanceof SubstitutionChunk ? missingValue : "";
final String chunkPrefix;
if (i == 1 && chunks.get(0) instanceof SubstitutionChunk) {
chunkPrefix = missingValue;
}
else if (i == 0) {
chunkPrefix = prefix;
} else {
chunkPrefix = "";
}
final String chunkSuffix;
if (nextIndex < chunks.size() && chunks.get(nextIndex) instanceof SubstitutionChunk) {
chunkSuffix = missingValue;
}
else if (nextIndex == chunks.size()) {
chunkSuffix = suffix;
}
else {
chunkSuffix = "";
}
final TextRange chunkRange = chunk.getTextRange().shiftRight(range.getStartOffset());
registrar.addPlace(chunkPrefix, chunkSuffix, expr, chunkRange);
}
@@ -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<? extends Class<? extends PsiElement>> elementsToInjectIn() {
return PyInjectionUtil.ELEMENTS_TO_INJECT_IN;
}
@Nullable
public abstract Language getInjectedLanguage(@NotNull PsiElement context);
}
@@ -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);