From 4602d09655c6231758f33af75bbc0fb0fbceb9fe Mon Sep 17 00:00:00 2001 From: Petr Date: Thu, 21 Mar 2024 14:56:16 +0100 Subject: [PATCH] PY-61639 Extracted PyHighlightingAnnotator GitOrigin-RevId: 432bcb87b77fa55f1ccb26000d91ae021cebcc6b --- .../com/jetbrains/python/ast/PyAstFile.java | 5 ++ .../resources/META-INF/PythonPsiImpl.xml | 1 - .../resources/messages/PyPsiBundle.properties | 4 -- .../jetbrains/python/psi/impl/PyFileImpl.java | 1 + .../python/validation/PyAnnotator.java | 37 +++--------- .../intellij.python.syntax.core.iml | 1 + .../resources/META-INF/PythonSyntaxCore.xml | 2 + .../messages/PySyntaxCoreBundle.properties | 8 ++- .../python/validation/PyAnnotatorBase.java | 58 +++++++++++++++++++ .../validation/PyFrontendAnnotator.java | 38 ++++++++++++ .../validation/PyHighlightingAnnotator.java | 29 +++++----- .../resources/META-INF/PythonSyntax.xml | 7 +++ .../validation/PyCompositeAnnotator.java | 5 +- .../validation/PyAnnotatingVisitor.java | 12 +--- python/src/intellij.python.community.impl.xml | 3 - 15 files changed, 145 insertions(+), 66 deletions(-) create mode 100644 python/python-syntax-core/src/com/jetbrains/python/validation/PyAnnotatorBase.java create mode 100644 python/python-syntax-core/src/com/jetbrains/python/validation/PyFrontendAnnotator.java rename python/{python-psi-impl => python-syntax-core}/src/com/jetbrains/python/validation/PyHighlightingAnnotator.java (71%) rename python/{ => python-syntax}/src/com/jetbrains/python/validation/PyCompositeAnnotator.java (76%) diff --git a/python/python-ast/src/com/jetbrains/python/ast/PyAstFile.java b/python/python-ast/src/com/jetbrains/python/ast/PyAstFile.java index 2b39b2a9944b..aff773fdb73a 100644 --- a/python/python-ast/src/com/jetbrains/python/ast/PyAstFile.java +++ b/python/python-ast/src/com/jetbrains/python/ast/PyAstFile.java @@ -62,4 +62,9 @@ public interface PyAstFile extends PyAstElement, PsiFile, PyAstDocStringOwner, A } return null; } + + @ApiStatus.Internal + default boolean isAcceptedFor(@NotNull Class visitorClass) { + return true; + } } diff --git a/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml b/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml index a3d834a0d829..921e0155cb78 100644 --- a/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml +++ b/python/python-psi-impl/resources/META-INF/PythonPsiImpl.xml @@ -8,7 +8,6 @@ - diff --git a/python/python-psi-impl/resources/messages/PyPsiBundle.properties b/python/python-psi-impl/resources/messages/PyPsiBundle.properties index 81a8a585ac12..94a8f37a66f2 100644 --- a/python/python-psi-impl/resources/messages/PyPsiBundle.properties +++ b/python/python-psi-impl/resources/messages/PyPsiBundle.properties @@ -131,7 +131,6 @@ ANN.assignment.expressions.within.a.comprehension.cannot.be.used.in.a.class.body ANN.assignment.expression.as.a.target=Assignment expression cannot be used as a target here ANN.assignment.expression.in.an.iterable=Assignment expression cannot be used in a comprehension iterable ANN.ignore.errors.like.this=Ignore errors like this -ANN.function.cannot.be.async=function \"{0}\" cannot be async ANN.python.does.not.support.yield.from.inside.async.functions=Python does not support 'yield from' inside async functions ANN.yield.outside.of.function='yield' outside of function ANN.non.empty.return.inside.asynchronous.generator=non-empty 'return' inside asynchronous generator @@ -965,9 +964,6 @@ INSP.dataclasses.method.should.be.called.on.dataclass.instances=''{0}'' method s INSP.dataclasses.method.should.be.called.on.attrs.instances=''{0}'' method should be called on attrs instances INSP.dataclasses.method.should.be.called.on.attrs.types=''{0}'' method should be called on attrs types -# PyHighlightingAnnotator -INSP.python.trailing.suffix.not.support=Python does not support a trailing ''{0}'' - # PyDeprecationInspection INSP.NAME.deprecated.function.class.or.module=Deprecated function, class, or module INSP.deprecation.abc.decorator.deprecated.use.alternative=''{0}'' is deprecated since Python 3.3. Use ''{1}'' with ''{2}'' instead diff --git a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyFileImpl.java index 3019f3882a90..6cbe15e69bc9 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/python-psi-impl/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -289,6 +289,7 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { // return element != null ? element : super.getNavigationElement(); //} + @Override public boolean isAcceptedFor(@NotNull Class visitorClass) { for (Language lang : getViewProvider().getLanguages()) { final List filters = PythonVisitorFilter.INSTANCE.allForLanguage(lang); diff --git a/python/python-psi-impl/src/com/jetbrains/python/validation/PyAnnotator.java b/python/python-psi-impl/src/com/jetbrains/python/validation/PyAnnotator.java index 13e85d77a5b6..0cd2f902f70e 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/validation/PyAnnotator.java +++ b/python/python-psi-impl/src/com/jetbrains/python/validation/PyAnnotator.java @@ -15,21 +15,22 @@ */ package com.jetbrains.python.validation; -import com.intellij.codeInspection.util.InspectionMessage; -import com.intellij.lang.ASTNode; import com.intellij.lang.annotation.AnnotationHolder; -import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.PyElementVisitor; -import org.jetbrains.annotations.NotNull; -public abstract class PyAnnotator extends PyElementVisitor { +public abstract class PyAnnotator extends PyElementVisitor implements PyAnnotatorBase { private final boolean myTestMode = ApplicationManager.getApplication().isUnitTestMode(); private AnnotationHolder _holder; + @Override + public boolean isTestMode() { + return myTestMode; + } + + @Override public AnnotationHolder getHolder() { return _holder; } @@ -38,6 +39,7 @@ public abstract class PyAnnotator extends PyElementVisitor { _holder = holder; } + @Override public synchronized void annotateElement(final PsiElement psiElement, final AnnotationHolder holder) { setHolder(holder); try { @@ -47,27 +49,4 @@ public abstract class PyAnnotator extends PyElementVisitor { setHolder(null); } } - - protected void markError(@NotNull PsiElement element, @NotNull @InspectionMessage String message) { - getHolder().newAnnotation(HighlightSeverity.ERROR, message).range(element).create(); - } - - protected void addHighlightingAnnotation(@NotNull PsiElement target, @NotNull TextAttributesKey key) { - addHighlightingAnnotation(target, key, HighlightSeverity.INFORMATION); - } - - protected void addHighlightingAnnotation(@NotNull PsiElement target, - @NotNull TextAttributesKey key, - @NotNull HighlightSeverity severity) { - final String message = myTestMode ? key.getExternalName() : null; - // CodeInsightTestFixture#testHighlighting doesn't consider annotations with severity level < INFO - final HighlightSeverity actualSeverity = - myTestMode && severity.myVal < HighlightSeverity.INFORMATION.myVal ? HighlightSeverity.INFORMATION : severity; - (message == null ? getHolder().newSilentAnnotation(actualSeverity) : getHolder().newAnnotation(actualSeverity, message)) - .range(target).textAttributes(key).create(); - } - - protected void addHighlightingAnnotation(@NotNull ASTNode target, @NotNull TextAttributesKey key) { - addHighlightingAnnotation(target.getPsi(), key); - } } diff --git a/python/python-syntax-core/intellij.python.syntax.core.iml b/python/python-syntax-core/intellij.python.syntax.core.iml index 800e1b4f85d7..1c1de3b33dd0 100644 --- a/python/python-syntax-core/intellij.python.syntax.core.iml +++ b/python/python-syntax-core/intellij.python.syntax.core.iml @@ -21,5 +21,6 @@ + \ No newline at end of file diff --git a/python/python-syntax-core/resources/META-INF/PythonSyntaxCore.xml b/python/python-syntax-core/resources/META-INF/PythonSyntaxCore.xml index 74835f97fe64..a0498d01d71c 100644 --- a/python/python-syntax-core/resources/META-INF/PythonSyntaxCore.xml +++ b/python/python-syntax-core/resources/META-INF/PythonSyntaxCore.xml @@ -13,5 +13,7 @@ + + \ No newline at end of file diff --git a/python/python-syntax-core/resources/messages/PySyntaxCoreBundle.properties b/python/python-syntax-core/resources/messages/PySyntaxCoreBundle.properties index 1c57a7c2155c..b36a9d9484c7 100644 --- a/python/python-syntax-core/resources/messages/PySyntaxCoreBundle.properties +++ b/python/python-syntax-core/resources/messages/PySyntaxCoreBundle.properties @@ -1,4 +1,10 @@ +### Annotators ### +ANN.function.cannot.be.async=function \"{0}\" cannot be async + ### Formatter formatter.panel.dict.alignment.do.not.align=Do not align formatter.panel.dict.alignment.align.on.colon=Align on colon -formatter.panel.dict.alignment.align.on.value=Align on value \ No newline at end of file +formatter.panel.dict.alignment.align.on.value=Align on value + +# PyHighlightingAnnotator +INSP.python.trailing.suffix.not.support=Python does not support a trailing ''{0}'' \ No newline at end of file diff --git a/python/python-syntax-core/src/com/jetbrains/python/validation/PyAnnotatorBase.java b/python/python-syntax-core/src/com/jetbrains/python/validation/PyAnnotatorBase.java new file mode 100644 index 000000000000..7e81c75bd805 --- /dev/null +++ b/python/python-syntax-core/src/com/jetbrains/python/validation/PyAnnotatorBase.java @@ -0,0 +1,58 @@ +package com.jetbrains.python.validation; + +import com.intellij.codeInspection.util.InspectionMessage; +import com.intellij.lang.ASTNode; +import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.lang.annotation.HighlightSeverity; +import com.intellij.openapi.editor.colors.TextAttributesKey; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.jetbrains.python.ast.PyAstFile; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Experimental +public interface PyAnnotatorBase { + @ApiStatus.Internal + static void runAnnotators(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder, PyAnnotatorBase[] annotators) { + PsiFile file = holder.getCurrentAnnotationSession().getFile(); + for (PyAnnotatorBase annotator : annotators) { + if (file instanceof PyAstFile && !((PyAstFile)file).isAcceptedFor(annotator.getClass())) continue; + annotator.annotateElement(psiElement, holder); + } + } + + @ApiStatus.Internal + boolean isTestMode(); + + AnnotationHolder getHolder(); + + void annotateElement(final PsiElement psiElement, final AnnotationHolder holder); + + @ApiStatus.Internal + default void markError(@NotNull PsiElement element, @NotNull @InspectionMessage String message) { + getHolder().newAnnotation(HighlightSeverity.ERROR, message).range(element).create(); + } + + @ApiStatus.Internal + default void addHighlightingAnnotation(@NotNull PsiElement target, @NotNull TextAttributesKey key) { + addHighlightingAnnotation(target, key, HighlightSeverity.INFORMATION); + } + + @ApiStatus.Internal + default void addHighlightingAnnotation(@NotNull PsiElement target, + @NotNull TextAttributesKey key, + @NotNull HighlightSeverity severity) { + final String message = isTestMode() ? key.getExternalName() : null; + // CodeInsightTestFixture#testHighlighting doesn't consider annotations with severity level < INFO + final HighlightSeverity actualSeverity = + isTestMode() && severity.myVal < HighlightSeverity.INFORMATION.myVal ? HighlightSeverity.INFORMATION : severity; + (message == null ? getHolder().newSilentAnnotation(actualSeverity) : getHolder().newAnnotation(actualSeverity, message)) + .range(target).textAttributes(key).create(); + } + + @ApiStatus.Internal + default void addHighlightingAnnotation(@NotNull ASTNode target, @NotNull TextAttributesKey key) { + addHighlightingAnnotation(target.getPsi(), key); + } +} diff --git a/python/python-syntax-core/src/com/jetbrains/python/validation/PyFrontendAnnotator.java b/python/python-syntax-core/src/com/jetbrains/python/validation/PyFrontendAnnotator.java new file mode 100644 index 000000000000..b6e82f99d4d1 --- /dev/null +++ b/python/python-syntax-core/src/com/jetbrains/python/validation/PyFrontendAnnotator.java @@ -0,0 +1,38 @@ +package com.jetbrains.python.validation; + +import com.intellij.lang.annotation.AnnotationHolder; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.psi.PsiElement; +import com.jetbrains.python.ast.PyAstElementVisitor; +import org.jetbrains.annotations.ApiStatus; + +@ApiStatus.Experimental +public class PyFrontendAnnotator extends PyAstElementVisitor implements PyAnnotatorBase { + private final boolean myTestMode = ApplicationManager.getApplication().isUnitTestMode(); + private AnnotationHolder _holder; + + @Override + public boolean isTestMode() { + return myTestMode; + } + + @Override + public AnnotationHolder getHolder() { + return _holder; + } + + public void setHolder(AnnotationHolder holder) { + _holder = holder; + } + + @Override + public synchronized void annotateElement(final PsiElement psiElement, final AnnotationHolder holder) { + setHolder(holder); + try { + psiElement.accept(this); + } + finally { + setHolder(null); + } + } +} diff --git a/python/python-psi-impl/src/com/jetbrains/python/validation/PyHighlightingAnnotator.java b/python/python-syntax-core/src/com/jetbrains/python/validation/PyHighlightingAnnotator.java similarity index 71% rename from python/python-psi-impl/src/com/jetbrains/python/validation/PyHighlightingAnnotator.java rename to python/python-syntax-core/src/com/jetbrains/python/validation/PyHighlightingAnnotator.java index 6676dae7bf1c..f32b36cf56a5 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/validation/PyHighlightingAnnotator.java +++ b/python/python-syntax-core/src/com/jetbrains/python/validation/PyHighlightingAnnotator.java @@ -21,9 +21,10 @@ import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.tree.TokenSet; -import com.jetbrains.python.PyPsiBundle; +import com.jetbrains.python.PySyntaxCoreBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonLanguage; +import com.jetbrains.python.ast.*; import com.jetbrains.python.highlighting.PyHighlighter; import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; @@ -31,10 +32,10 @@ import org.jetbrains.annotations.Nullable; import java.util.Optional; -public final class PyHighlightingAnnotator extends PyAnnotator implements HighlightRangeExtension { +public final class PyHighlightingAnnotator extends PyFrontendAnnotator implements HighlightRangeExtension { @Override - public void visitPyFunction(@NotNull PyFunction node) { + public void visitPyFunction(@NotNull PyAstFunction node) { if (node.isAsyncAllowed()) { highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD); } @@ -43,57 +44,57 @@ public final class PyHighlightingAnnotator extends PyAnnotator implements Highli .ofNullable(node.getNode()) .map(astNode -> astNode.findChildByType(PyTokenTypes.ASYNC_KEYWORD)) .ifPresent(asyncNode -> getHolder().newAnnotation(HighlightSeverity.ERROR, - PyPsiBundle.message("ANN.function.cannot.be.async", node.getName())).range(asyncNode).create()); + PySyntaxCoreBundle.message("ANN.function.cannot.be.async", node.getName())).range(asyncNode).create()); } } @Override - public void visitPyNumericLiteralExpression(@NotNull PyNumericLiteralExpression node) { + public void visitPyNumericLiteralExpression(@NotNull PyAstNumericLiteralExpression node) { String suffix = node.getIntegerLiteralSuffix(); if (suffix == null || "l".equalsIgnoreCase(suffix)) return; if (node.getContainingFile().getLanguage() != PythonLanguage.getInstance()) return; - getHolder().newAnnotation(HighlightSeverity.ERROR, PyPsiBundle.message("INSP.python.trailing.suffix.not.support", suffix)) + getHolder().newAnnotation(HighlightSeverity.ERROR, PySyntaxCoreBundle.message("INSP.python.trailing.suffix.not.support", suffix)) .range(node).create(); } @Override - public void visitPyForStatement(@NotNull PyForStatement node) { + public void visitPyForStatement(@NotNull PyAstForStatement node) { highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD); } @Override - public void visitPyWithStatement(@NotNull PyWithStatement node) { + public void visitPyWithStatement(@NotNull PyAstWithStatement node) { highlightKeyword(node, PyTokenTypes.ASYNC_KEYWORD); } @Override - public void visitPyPrefixExpression(@NotNull PyPrefixExpression node) { + public void visitPyPrefixExpression(@NotNull PyAstPrefixExpression node) { highlightKeyword(node, PyTokenTypes.AWAIT_KEYWORD); } @Override - public void visitPyComprehensionElement(@NotNull PyComprehensionElement node) { + public void visitPyComprehensionElement(@NotNull PyAstComprehensionElement node) { highlightKeywords(node, PyTokenTypes.ASYNC_KEYWORD); } @Override - public void visitPyMatchStatement(@NotNull PyMatchStatement node) { + public void visitPyMatchStatement(@NotNull PyAstMatchStatement node) { highlightKeyword(node, PyTokenTypes.MATCH_KEYWORD); } @Override - public void visitPyCaseClause(@NotNull PyCaseClause node) { + public void visitPyCaseClause(@NotNull PyAstCaseClause node) { highlightKeyword(node, PyTokenTypes.CASE_KEYWORD); } @Override - public void visitPyTypeAliasStatement(@NotNull PyTypeAliasStatement node) { + public void visitPyTypeAliasStatement(@NotNull PyAstTypeAliasStatement node) { highlightKeyword(node, PyTokenTypes.TYPE_KEYWORD); } @Override public boolean isForceHighlightParents(@NotNull PsiFile file) { - return file instanceof PyFile; + return file instanceof PyAstFile; } private void highlightKeyword(@NotNull PsiElement node, @NotNull PyElementType elementType) { diff --git a/python/python-syntax/resources/META-INF/PythonSyntax.xml b/python/python-syntax/resources/META-INF/PythonSyntax.xml index c4535d270843..ff22490320eb 100644 --- a/python/python-syntax/resources/META-INF/PythonSyntax.xml +++ b/python/python-syntax/resources/META-INF/PythonSyntax.xml @@ -15,5 +15,12 @@ + + + + + + + \ No newline at end of file diff --git a/python/src/com/jetbrains/python/validation/PyCompositeAnnotator.java b/python/python-syntax/src/com/jetbrains/python/validation/PyCompositeAnnotator.java similarity index 76% rename from python/src/com/jetbrains/python/validation/PyCompositeAnnotator.java rename to python/python-syntax/src/com/jetbrains/python/validation/PyCompositeAnnotator.java index 7dd2738d100c..ab3399ec81b9 100644 --- a/python/src/com/jetbrains/python/validation/PyCompositeAnnotator.java +++ b/python/python-syntax/src/com/jetbrains/python/validation/PyCompositeAnnotator.java @@ -7,11 +7,10 @@ import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; - public final class PyCompositeAnnotator implements Annotator { @Override public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { - PyAnnotator[] annotators = ExtensionPointName.create("Pythonid.pyAnnotator").getExtensions(); - PyAnnotatingVisitor.runAnnotators(element, holder, annotators); + PyAnnotatorBase[] annotators = ExtensionPointName.create("Pythonid.pyAnnotator").getExtensions(); + PyAnnotatorBase.runAnnotators(element, holder, annotators); } } diff --git a/python/src/com/jetbrains/python/validation/PyAnnotatingVisitor.java b/python/src/com/jetbrains/python/validation/PyAnnotatingVisitor.java index 3c2c22c175f0..dbb8c59aaa49 100644 --- a/python/src/com/jetbrains/python/validation/PyAnnotatingVisitor.java +++ b/python/src/com/jetbrains/python/validation/PyAnnotatingVisitor.java @@ -6,8 +6,6 @@ import com.intellij.lang.annotation.Annotator; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.jetbrains.python.psi.impl.PyFileImpl; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -51,14 +49,6 @@ public final class PyAnnotatingVisitor implements Annotator, DumbAware { @Override public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder) { - runAnnotators(psiElement, holder, myAnnotators); - } - - static void runAnnotators(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder, PyAnnotator[] annotators) { - PsiFile file = holder.getCurrentAnnotationSession().getFile(); - for (PyAnnotator annotator : annotators) { - if (file instanceof PyFileImpl && !((PyFileImpl)file).isAcceptedFor(annotator.getClass())) continue; - annotator.annotateElement(psiElement, holder); - } + PyAnnotatorBase.runAnnotators(psiElement, holder, myAnnotators); } } diff --git a/python/src/intellij.python.community.impl.xml b/python/src/intellij.python.community.impl.xml index 030dc4f0c687..9204dbf2ff72 100644 --- a/python/src/intellij.python.community.impl.xml +++ b/python/src/intellij.python.community.impl.xml @@ -97,7 +97,6 @@ order="before pythonDocumentationProvider"/> - - @@ -702,7 +700,6 @@ -