PY-61639 Extracted PyHighlightingAnnotator

GitOrigin-RevId: 432bcb87b77fa55f1ccb26000d91ae021cebcc6b
This commit is contained in:
Petr
2024-03-21 14:56:16 +01:00
committed by intellij-monorepo-bot
parent d396a19918
commit 4602d09655
15 changed files with 145 additions and 66 deletions

View File

@@ -62,4 +62,9 @@ public interface PyAstFile extends PyAstElement, PsiFile, PyAstDocStringOwner, A
}
return null;
}
@ApiStatus.Internal
default boolean isAcceptedFor(@NotNull Class<?> visitorClass) {
return true;
}
}

View File

@@ -8,7 +8,6 @@
<projectService serviceImplementation="com.jetbrains.python.findUsages.PyFindUsagesOptions"/>
<highlightRangeExtension implementation="com.jetbrains.python.validation.PyHighlightingAnnotator"/>
<!-- We run it before FilePathCompletionContributor (id="filePath") to get help text about full path results in the extended completion -->
<psi.referenceContributor implementation="com.jetbrains.python.codeInsight.PySoftFileReferenceContributor" language="Python"
order="before filePath"/>

View File

@@ -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

View File

@@ -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<PythonVisitorFilter> filters = PythonVisitorFilter.INSTANCE.allForLanguage(lang);

View File

@@ -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);
}
}

View File

@@ -21,5 +21,6 @@
<orderEntry type="module" module-name="intellij.platform.projectModel.impl" />
<orderEntry type="module" module-name="intellij.platform.analysis" />
<orderEntry type="module" module-name="intellij.platform.ide.core.impl" />
<orderEntry type="module" module-name="intellij.platform.analysis.impl" />
</component>
</module>

View File

@@ -13,5 +13,7 @@
<preFormatProcessor implementation="com.jetbrains.python.formatter.PyPreFormatProcessor"/>
<postFormatProcessor implementation="com.jetbrains.python.formatter.PyTrailingBlankLinesPostFormatProcessor"/>
<postFormatProcessor implementation="com.jetbrains.python.formatter.PyFromImportPostFormatProcessor"/>
<highlightRangeExtension implementation="com.jetbrains.python.validation.PyHighlightingAnnotator"/>
</extensions>
</idea-plugin>

View File

@@ -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
formatter.panel.dict.alignment.align.on.value=Align on value
# PyHighlightingAnnotator
INSP.python.trailing.suffix.not.support=Python does not support a trailing ''{0}''

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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) {

View File

@@ -15,5 +15,12 @@
<typedHandler implementation="com.jetbrains.python.codeInsight.PyKeywordTypedHandler" id="pyCommaAfterKwd"/>
<typedHandler implementation="com.jetbrains.python.editor.PythonSpaceHandler"/>
<backspaceHandlerDelegate implementation="com.jetbrains.python.codeInsight.editorActions.PyTripleQuoteBackspaceDelegate"/>
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyCompositeAnnotator"/>
</extensions>
<extensions defaultExtensionNs="Pythonid">
<pyAnnotator implementation="com.jetbrains.python.validation.PyHighlightingAnnotator"/>
</extensions>
<extensionPoints>
<extensionPoint qualifiedName="Pythonid.pyAnnotator" interface="com.jetbrains.python.validation.PyAnnotatorBase" dynamic="true"/>
</extensionPoints>
</idea-plugin>

View File

@@ -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.<PyAnnotator>create("Pythonid.pyAnnotator").getExtensions();
PyAnnotatingVisitor.runAnnotators(element, holder, annotators);
PyAnnotatorBase[] annotators = ExtensionPointName.<PyAnnotatorBase>create("Pythonid.pyAnnotator").getExtensions();
PyAnnotatorBase.runAnnotators(element, holder, annotators);
}
}

View File

@@ -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);
}
}

View File

@@ -97,7 +97,6 @@
order="before pythonDocumentationProvider"/>
<lang.emacs language="Python" implementationClass="com.jetbrains.python.editor.PyEmacsHandler"/>
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyAnnotatingVisitor"/>
<annotator language="Python" implementationClass="com.jetbrains.python.validation.PyCompositeAnnotator"/>
<annotator language="Python" implementationClass="com.jetbrains.python.inspections.PyCompatibilityInspectionAdvertiser"/>
<enterBetweenBracesDelegate language="Python"
@@ -600,7 +599,6 @@
<extensionPoint qualifiedName="Pythonid.unresolvedReferenceQuickFixProvider"
interface="com.jetbrains.python.inspections.PyUnresolvedReferenceQuickFixProvider"
dynamic="true"/>
<extensionPoint qualifiedName="Pythonid.pyAnnotator" interface="com.jetbrains.python.validation.PyAnnotator" dynamic="true"/>
<extensionPoint qualifiedName="Pythonid.documentationLinkProvider"
interface="com.jetbrains.python.documentation.PythonDocumentationLinkProvider"
dynamic="true"/>
@@ -702,7 +700,6 @@
<pyAnnotator implementation="com.jetbrains.python.validation.StarAnnotator"/>
<pyAnnotator implementation="com.jetbrains.python.validation.StringLiteralQuotesAnnotator"/>
<pyAnnotator implementation="com.jetbrains.python.validation.FStringsAnnotator"/>
<pyAnnotator implementation="com.jetbrains.python.validation.PyHighlightingAnnotator"/>
<pyAnnotator implementation="com.jetbrains.python.validation.PyPatternAnnotator"/>
<pyAnnotator implementation="com.jetbrains.python.validation.PyTryExceptAnnotator"/>
<pyAnnotator implementation="com.jetbrains.python.validation.PyLocalVariableAnnotator"/>