diff --git a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java index 2d77cc8f48af..823ae11d42d8 100644 --- a/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/PyInjectionUtil.java @@ -15,7 +15,6 @@ */ 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; @@ -23,8 +22,6 @@ import com.intellij.psi.PsiComment; 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; @@ -32,7 +29,6 @@ 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.*; @@ -43,18 +39,14 @@ import static com.jetbrains.python.inspections.PyStringFormatParser.*; public class PyInjectionUtil { public static class InjectionResult { - public static final InjectionResult EMPTY = new InjectionResult(false, true, Collections.emptyList()); + public static final InjectionResult EMPTY = new InjectionResult(false, true); private final boolean myInjected; private final boolean myStrict; - private final List myCollectedFStrings; - public InjectionResult(boolean injected, boolean strict) {this(injected, strict, Collections.emptyList());} - - private InjectionResult(boolean injected, boolean strict, @NotNull List nodes) { + public InjectionResult(boolean injected, boolean strict) { myInjected = injected; myStrict = strict; - myCollectedFStrings = nodes; } public boolean isInjected() { @@ -66,9 +58,7 @@ public class PyInjectionUtil { } public InjectionResult append(@NotNull InjectionResult result) { - return new InjectionResult(myInjected || result.isInjected(), - myStrict && result.isStrict(), - ContainerUtil.concat(myCollectedFStrings, result.myCollectedFStrings)); + return new InjectionResult(myInjected || result.isInjected(), myStrict && result.isStrict()); } } @@ -104,12 +94,6 @@ public class PyInjectionUtil { 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; } @@ -165,29 +149,23 @@ 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); - final TextRange contentRange = nodeInfo.getContentRange(); + for (PyStringElement stringElem : expr.getStringElements()) { + final int nodeOffsetInParent = stringElem.getTextOffset() - expr.getTextRange().getStartOffset(); + final TextRange contentRange = stringElem.getContentRange(); final int contentStartOffset = contentRange.getStartOffset(); - if (formatting != Formatting.NONE || nodeInfo.isFormatted()) { - // Each range is relative to the start of the string node + if (formatting != Formatting.NONE || stringElem.isFormatted()) { + // Each range is relative to the start of the string element final List subsRanges; if (formatting != Formatting.NONE) { - final String content = nodeInfo.getContent(); + final String content = stringElem.getContent(); subsRanges = StreamEx.of(formatting == Formatting.NEW_STYLE ? parseNewStyleFormat(content) : parsePercentFormat(content)) .select(SubstitutionChunk.class) .map(chunk -> chunk.getTextRange().shiftRight(contentStartOffset)) .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}} - .map(f -> TextRange.create(f.getLeftBraceOffset(), - Math.max(f.getRightBraceOffset() + 1, f.getContentEndOffset()))) + subsRanges = StreamEx.of(((PyFormattedStringElement)stringElem).getFragments()) + .map(f -> f.getTextRange().shiftLeft(stringElem.getTextOffset())) .toList(); } if (!subsRanges.isEmpty()) { @@ -238,7 +216,7 @@ public class PyInjectionUtil { injected = true; } } - return new InjectionResult(injected, strict, hasFormattedNodes ? Collections.singletonList(expr) : Collections.emptyList()); + return new InjectionResult(injected, strict); } else if (element instanceof PyParenthesizedExpression) { final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression(); diff --git a/python/src/com/jetbrains/python/codeInsight/fstrings/PyFStringsInjector.java b/python/src/com/jetbrains/python/codeInsight/fstrings/PyFStringsInjector.java deleted file mode 100644 index a0a0125696ba..000000000000 --- a/python/src/com/jetbrains/python/codeInsight/fstrings/PyFStringsInjector.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jetbrains.python.codeInsight.fstrings; - -import com.intellij.codeInsight.generation.CommentByLineCommentHandler; -import com.intellij.lang.ASTNode; -import com.intellij.lang.Language; -import com.intellij.lang.injection.InjectedLanguageManager; -import com.intellij.lang.injection.MultiHostRegistrar; -import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.jetbrains.python.codeInsight.PyInjectorBase; -import com.jetbrains.python.codeInsight.fstrings.FStringParser.Fragment; -import com.jetbrains.python.documentation.doctest.PyDocstringLanguageDialect; -import com.jetbrains.python.psi.PyStringLiteralExpression; -import com.jetbrains.python.psi.PyUtil.StringNodeInfo; -import one.util.streamex.StreamEx; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; - -import static com.jetbrains.python.psi.PyUtil.as; - -/** - * @author Mikhail Golubev - */ -public class PyFStringsInjector extends PyInjectorBase { - @Override - public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { - final PyStringLiteralExpression pyString = as(context, PyStringLiteralExpression.class); - if (pyString == null) { - return; - } - - injectFStringFragments(registrar, pyString); - } - - public static void injectFStringFragments(@NotNull MultiHostRegistrar registrar, @NotNull PyStringLiteralExpression pyString) { - final PyDocstringLanguageDialect docstringLanguage = PyDocstringLanguageDialect.getInstance(); - for (ASTNode node : pyString.getStringNodes()) { - final int relNodeOffset = node.getTextRange().getStartOffset() - pyString.getTextRange().getStartOffset(); - for (Fragment offsets : getInjectionRanges(node)) { - if (offsets.containsNamedUnicodeEscape()) continue; - registrar.startInjecting(docstringLanguage); - registrar.addPlace(null, null, pyString, offsets.getContentRange().shiftRight(relNodeOffset)); - registrar.doneInjecting(); - } - } - - disableCommentingInFragments(pyString); - } - - private static void disableCommentingInFragments(@NotNull PyStringLiteralExpression pyString) { - final Project project = pyString.getProject(); - final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(project); - final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project); - final PyDocstringLanguageDialect docstringLanguage = PyDocstringLanguageDialect.getInstance(); - - StreamEx.of(injectedLanguageManager.getCachedInjectedDocumentsInRange(pyString.getContainingFile(), pyString.getTextRange())) - .map(window -> documentManager.getPsiFile(window)) - .nonNull() - .filter(file -> file.getLanguage().isKindOf(docstringLanguage)) - .forEach(CommentByLineCommentHandler::markInjectedFileUnsuitableForLineComment); - } - - @NotNull - private static List getInjectionRanges(@NotNull ASTNode node) { - final StringNodeInfo nodeInfo = new StringNodeInfo(node); - if (nodeInfo.isFormatted()) { - return FStringParser.parse(node.getText()).getFragments(); - } - return Collections.emptyList(); - } - - @Nullable - @Override - public Language getInjectedLanguage(@NotNull PsiElement context) { - return context instanceof PyStringLiteralExpression? PyDocstringLanguageDialect.getInstance() : null; - } -} diff --git a/python/testSrc/com/jetbrains/python/Py3ResolveTest.java b/python/testSrc/com/jetbrains/python/Py3ResolveTest.java index 4ece2aaaf2fd..8a94671a3f40 100644 --- a/python/testSrc/com/jetbrains/python/Py3ResolveTest.java +++ b/python/testSrc/com/jetbrains/python/Py3ResolveTest.java @@ -649,4 +649,9 @@ public class Py3ResolveTest extends PyResolveTestCase { public void testFStringInsideAssertStatement() { runWithLanguageLevel(LanguageLevel.PYTHON36, () -> assertResolvesTo(PyParameter.class, "name")); } + + // PY-21493 + public void testRegexpAndFStringCombined() { + runWithLanguageLevel(LanguageLevel.PYTHON36, () -> assertResolvesTo(PyTargetExpression.class, "foo")); + } } diff --git a/python/testSrc/com/jetbrains/python/PyInjectionResolveTest.java b/python/testSrc/com/jetbrains/python/PyInjectionResolveTest.java index e8585971bad9..9ede52a19748 100644 --- a/python/testSrc/com/jetbrains/python/PyInjectionResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyInjectionResolveTest.java @@ -24,7 +24,6 @@ import com.jetbrains.python.fixtures.PyResolveTestCase; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyReferenceOwner; -import com.jetbrains.python.psi.PyTargetExpression; /** * @author Mikhail Golubev @@ -75,9 +74,4 @@ public class PyInjectionResolveTest extends PyResolveTestCase { public void testFunctionTypeCommentReturnTypeReference() { assertResolvesTo(PyClass.class, "MyClass"); } - - // PY-21493 - public void testRegexpAndFStringCombined() { - runWithLanguageLevel(LanguageLevel.PYTHON36, () -> assertResolvesTo(PyTargetExpression.class, "foo")); - } }