mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-21493 PY-23270 Python IntelliLang injectors are aware of f-strings
and considered before the dedicated injector for f-strings takes place. Also, PyInjectionUtil.registerStringLiteralInjection now takes care of f-string injections itself.
This commit is contained in:
@@ -7,7 +7,8 @@
|
||||
</extensions>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<patterns.patternClass className="com.jetbrains.python.patterns.PythonPatterns" alias="py"/>
|
||||
<multiHostInjector implementation="com.jetbrains.python.intelliLang.PyConfigurationInjector"/>
|
||||
<multiHostInjector implementation="com.jetbrains.python.intelliLang.PyTemporaryInjector"/>
|
||||
<!-- These must be registered before PyFStringsInjector, otherwise it won't allow to inject anything else inside f-strings -->
|
||||
<multiHostInjector implementation="com.jetbrains.python.intelliLang.PyConfigurationInjector" order="before f-strings"/>
|
||||
<multiHostInjector implementation="com.jetbrains.python.intelliLang.PyTemporaryInjector" order="before f-strings"/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
@@ -633,7 +633,7 @@
|
||||
serviceImplementation="com.jetbrains.python.PythonFoldingSettings"/>
|
||||
|
||||
<!-- f-strings -->
|
||||
<multiHostInjector implementation="com.jetbrains.python.codeInsight.fstrings.PyFStringsInjector"/>
|
||||
<multiHostInjector implementation="com.jetbrains.python.codeInsight.fstrings.PyFStringsInjector" id="f-strings"/>
|
||||
|
||||
<!-- postfix templates!-->
|
||||
<codeInsight.template.postfixTemplateProvider language="Python"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.jetbrains.python.codeInsight;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.injection.MultiHostRegistrar;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiComment;
|
||||
@@ -23,6 +24,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.fstrings.FStringParser;
|
||||
import com.jetbrains.python.codeInsight.fstrings.PyFStringsInjector;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyCallExpressionNavigator;
|
||||
import one.util.streamex.StreamEx;
|
||||
@@ -30,6 +32,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.jetbrains.python.inspections.PyStringFormatParser.*;
|
||||
@@ -40,14 +43,18 @@ import static com.jetbrains.python.inspections.PyStringFormatParser.*;
|
||||
public class PyInjectionUtil {
|
||||
|
||||
public static class InjectionResult {
|
||||
public static InjectionResult EMPTY = new InjectionResult(false, true);
|
||||
public static final InjectionResult EMPTY = new InjectionResult(false, true, Collections.emptyList());
|
||||
|
||||
private final boolean myInjected;
|
||||
private final boolean myStrict;
|
||||
private final List<PyStringLiteralExpression> myCollectedFStrings;
|
||||
|
||||
public InjectionResult(boolean injected, boolean strict) {
|
||||
public InjectionResult(boolean injected, boolean strict) {this(injected, strict, Collections.emptyList());}
|
||||
|
||||
private InjectionResult(boolean injected, boolean strict, @NotNull List<PyStringLiteralExpression> nodes) {
|
||||
myInjected = injected;
|
||||
myStrict = strict;
|
||||
myCollectedFStrings = nodes;
|
||||
}
|
||||
|
||||
public boolean isInjected() {
|
||||
@@ -59,7 +66,9 @@ public class PyInjectionUtil {
|
||||
}
|
||||
|
||||
public InjectionResult append(@NotNull InjectionResult result) {
|
||||
return new InjectionResult(myInjected || result.isInjected(), myStrict && result.isStrict());
|
||||
return new InjectionResult(myInjected || result.isInjected(),
|
||||
myStrict && result.isStrict(),
|
||||
ContainerUtil.concat(myCollectedFStrings, result.myCollectedFStrings));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,8 +96,21 @@ public class PyInjectionUtil {
|
||||
* string concatenations or formatting.
|
||||
*/
|
||||
@NotNull
|
||||
public static InjectionResult registerStringLiteralInjection(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) {
|
||||
return processStringLiteral(element, registrar, "", "", Formatting.NONE);
|
||||
public static InjectionResult registerStringLiteralInjection(@NotNull PsiElement element,
|
||||
@NotNull MultiHostRegistrar registrar,
|
||||
@NotNull Language language) {
|
||||
registrar.startInjecting(language);
|
||||
final InjectionResult result = processStringLiteral(element, registrar, "", "", Formatting.NONE);
|
||||
if (result.isInjected()) {
|
||||
registrar.doneInjecting();
|
||||
}
|
||||
|
||||
// Only one injector can process the given element, thus we should additionally
|
||||
// take care of f-string here instead of PyFStringsInjector
|
||||
for (PyStringLiteralExpression literal: result.myCollectedFStrings) {
|
||||
PyFStringsInjector.injectFStringFragments(registrar, literal);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static boolean isStringLiteralPart(@NotNull PsiElement element, @Nullable PsiElement context) {
|
||||
@@ -143,6 +165,7 @@ public class PyInjectionUtil {
|
||||
boolean injected = false;
|
||||
boolean strict = true;
|
||||
final PyStringLiteralExpression expr = (PyStringLiteralExpression)element;
|
||||
boolean hasFormattedNodes = false;
|
||||
for (ASTNode node : expr.getStringNodes()) {
|
||||
final int nodeOffsetInParent = node.getStartOffset() - expr.getTextRange().getStartOffset();
|
||||
final PyUtil.StringNodeInfo nodeInfo = new PyUtil.StringNodeInfo(node);
|
||||
@@ -159,6 +182,7 @@ public class PyInjectionUtil {
|
||||
.toList();
|
||||
}
|
||||
else {
|
||||
hasFormattedNodes = true;
|
||||
// f-string fragment parser handles string literal prefix and opening quotes itself
|
||||
subsRanges = StreamEx.of(FStringParser.parse(node.getText()).getFragments())
|
||||
.filter(f -> f.getDepth() == 1) // don't consider nested fragments like {foo:{bar}}
|
||||
@@ -214,7 +238,7 @@ public class PyInjectionUtil {
|
||||
injected = true;
|
||||
}
|
||||
}
|
||||
return new InjectionResult(injected, strict);
|
||||
return new InjectionResult(injected, strict, hasFormattedNodes ? Collections.singletonList(expr) : Collections.emptyList());
|
||||
}
|
||||
else if (element instanceof PyParenthesizedExpression) {
|
||||
final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression();
|
||||
|
||||
@@ -47,12 +47,7 @@ public abstract class PyInjectorBase implements MultiHostInjector {
|
||||
if (language != null) {
|
||||
final PsiElement element = PyInjectionUtil.getLargestStringLiteral(context);
|
||||
if (element != null) {
|
||||
registrar.startInjecting(language);
|
||||
final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(element, registrar);
|
||||
if (result.isInjected()) {
|
||||
registrar.doneInjecting();
|
||||
}
|
||||
return result;
|
||||
return PyInjectionUtil.registerStringLiteralInjection(element, registrar, language);
|
||||
}
|
||||
}
|
||||
return PyInjectionUtil.InjectionResult.EMPTY;
|
||||
|
||||
@@ -46,7 +46,7 @@ public class PyFStringsInjector extends PyInjectorBase {
|
||||
injectFStringFragments(registrar, pyString);
|
||||
}
|
||||
|
||||
public static void injectFStringFragments(@NotNull MultiHostRegistrar registrar, PyStringLiteralExpression pyString) {
|
||||
public static void injectFStringFragments(@NotNull MultiHostRegistrar registrar, @NotNull PyStringLiteralExpression pyString) {
|
||||
for (ASTNode node : pyString.getStringNodes()) {
|
||||
final int relNodeOffset = node.getTextRange().getStartOffset() - pyString.getTextRange().getStartOffset();
|
||||
for (Fragment offsets : getInjectionRanges(node)) {
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.codeInsight.PyInjectionUtil;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.codeInsight.fstrings.PyFStringsInjector;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
@@ -97,20 +96,13 @@ public class PythonRegexpInjector implements MultiHostInjector {
|
||||
|
||||
private static void injectRegexpLanguage(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context, boolean verbose) {
|
||||
final Language language = verbose ? PythonVerboseRegexpLanguage.INSTANCE : PythonRegexpLanguage.INSTANCE;
|
||||
registrar.startInjecting(language);
|
||||
final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(context, registrar);
|
||||
if (result.isInjected()) {
|
||||
registrar.doneInjecting();
|
||||
if (!result.isStrict()) {
|
||||
final PsiFile file = InjectedLanguageUtil.getCachedInjectedFileWithLanguage(context, language);
|
||||
if (file != null) {
|
||||
file.putUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
|
||||
}
|
||||
final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(context, registrar, language);
|
||||
if (result.isInjected() && !result.isStrict()) {
|
||||
final PsiFile file = InjectedLanguageUtil.getCachedInjectedFileWithLanguage(context, language);
|
||||
if (file != null) {
|
||||
file.putUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
if (context instanceof PyStringLiteralExpression) {
|
||||
PyFStringsInjector.injectFStringFragments(registrar, (PyStringLiteralExpression)context);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
Reference in New Issue
Block a user