mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-31442 Use PyStringElement API in PyInjectionUtil, finally remove PyFStringsInjector
This commit is contained in:
@@ -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<PyStringLiteralExpression> myCollectedFStrings;
|
||||
|
||||
public InjectionResult(boolean injected, boolean strict) {this(injected, strict, Collections.emptyList());}
|
||||
|
||||
private InjectionResult(boolean injected, boolean strict, @NotNull List<PyStringLiteralExpression> 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<TextRange> 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();
|
||||
|
||||
@@ -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<Fragment> 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;
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user