Converted JetPsiChecker to Kotlin.

This commit is contained in:
Evgeny Gerashchenko
2014-11-28 20:52:34 +03:00
parent 2f283da7dd
commit cbebb17eca
3 changed files with 170 additions and 209 deletions
@@ -1,5 +1,5 @@
/* /*
* Copyright 2010-2013 JetBrains s.r.o. * Copyright 2010-2014 JetBrains s.r.o.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -14,267 +14,229 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.jet.plugin.highlighter; package org.jetbrains.jet.plugin.highlighter
import com.intellij.codeInsight.daemon.impl.HighlightRangeExtension; import com.intellij.codeInsight.daemon.impl.HighlightRangeExtension
import com.intellij.codeInsight.intention.EmptyIntentionAction; import com.intellij.codeInsight.intention.EmptyIntentionAction
import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.Annotation
import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.Annotator; import com.intellij.lang.annotation.Annotator
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.TextRange
import com.intellij.psi.MultiRangeReference; import com.intellij.psi.MultiRangeReference
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile
import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReference
import com.intellij.xml.util.XmlStringUtil; import com.intellij.xml.util.XmlStringUtil
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.TestOnly
import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.analyzer.AnalysisResult
import org.jetbrains.annotations.TestOnly; import org.jetbrains.jet.lang.diagnostics.Diagnostic
import org.jetbrains.jet.analyzer.AnalysisResult; import org.jetbrains.jet.lang.diagnostics.Errors
import org.jetbrains.jet.lang.diagnostics.Diagnostic; import org.jetbrains.jet.lang.diagnostics.Severity
import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.jet.lang.diagnostics.Severity; import org.jetbrains.jet.lang.psi.JetCodeFragment
import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages; import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.psi.JetCodeFragment; import org.jetbrains.jet.lang.psi.JetReferenceExpression
import org.jetbrains.jet.lang.psi.JetFile; import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.psi.JetReferenceExpression; import org.jetbrains.jet.lang.resolve.Diagnostics
import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.plugin.util.ProjectRootsUtil
import org.jetbrains.jet.lang.resolve.Diagnostics; import org.jetbrains.jet.plugin.actions.internal.KotlinInternalMode
import org.jetbrains.jet.plugin.util.ProjectRootsUtil; import org.jetbrains.jet.plugin.caches.resolve.*
import org.jetbrains.jet.plugin.actions.internal.KotlinInternalMode; import org.jetbrains.jet.plugin.quickfix.JetIntentionActionsFactory
import org.jetbrains.jet.plugin.caches.resolve.ResolvePackage; import org.jetbrains.jet.plugin.quickfix.QuickFixes
import org.jetbrains.jet.plugin.quickfix.JetIntentionActionsFactory; import kotlin.platform.platformStatic
import org.jetbrains.jet.plugin.quickfix.QuickFixes;
import java.util.Collection; public open class JetPsiChecker : Annotator, HighlightRangeExtension {
import java.util.List;
public class JetPsiChecker implements Annotator, HighlightRangeExtension { override fun annotate(element: PsiElement, holder: AnnotationHolder) {
private static boolean isNamesHighlightingEnabled = true; if (!(ProjectRootsUtil.isInProjectOrLibSource(element) || element.getContainingFile() is JetCodeFragment)) return
@TestOnly getBeforeAnalysisVisitors(holder).forEach { visitor -> element.accept(visitor) }
public static void setNamesHighlightingEnabled(boolean namesHighlightingEnabled) {
isNamesHighlightingEnabled = namesHighlightingEnabled;
}
public static boolean isNamesHighlightingEnabled() { val file = element.getContainingFile() as JetFile
return isNamesHighlightingEnabled;
}
static void highlightName(@NotNull AnnotationHolder holder, val analysisResult = file.analyzeFullyAndGetResult()
@NotNull PsiElement psiElement,
@NotNull TextAttributesKey attributesKey) {
if (isNamesHighlightingEnabled()) {
holder.createInfoAnnotation(psiElement, null).setTextAttributes(attributesKey);
}
}
private static HighlightingVisitor[] getBeforeAnalysisVisitors(AnnotationHolder holder) {
return new HighlightingVisitor[]{
new SoftKeywordsHighlightingVisitor(holder),
new LabelsHighlightingVisitor(holder),
};
}
private static HighlightingVisitor[] getAfterAnalysisVisitor(AnnotationHolder holder, BindingContext bindingContext) {
return new AfterAnalysisHighlightingVisitor[]{
new PropertiesHighlightingVisitor(holder, bindingContext),
new FunctionsHighlightingVisitor(holder, bindingContext),
new VariablesHighlightingVisitor(holder, bindingContext),
new TypeKindHighlightingVisitor(holder, bindingContext),
new DeprecatedAnnotationVisitor(holder, bindingContext),
};
}
@Override
public void annotate(@NotNull PsiElement element, @NotNull AnnotationHolder holder) {
if (!(ProjectRootsUtil.isInProjectOrLibSource(element) || element.getContainingFile() instanceof JetCodeFragment)) return;
for (HighlightingVisitor visitor : getBeforeAnalysisVisitors(holder)) {
element.accept(visitor);
}
JetFile file = (JetFile) element.getContainingFile();
AnalysisResult analysisResult = ResolvePackage.analyzeFullyAndGetResult(file);
if (analysisResult.isError()) { if (analysisResult.isError()) {
throw new ProcessCanceledException(analysisResult.getError()); throw ProcessCanceledException(analysisResult.error)
} }
BindingContext bindingContext = analysisResult.getBindingContext(); val bindingContext = analysisResult.bindingContext
for (HighlightingVisitor visitor : getAfterAnalysisVisitor(holder, bindingContext)) { getAfterAnalysisVisitor(holder, bindingContext).forEach { visitor -> element.accept(visitor) }
element.accept(visitor);
}
annotateElement(element, holder, bindingContext.getDiagnostics()); annotateElement(element, holder, bindingContext.getDiagnostics())
} }
public static void annotateElement(PsiElement element, AnnotationHolder holder, Diagnostics diagnostics) { override fun isForceHighlightParents(file: PsiFile): Boolean {
if (ProjectRootsUtil.isInProjectSource(element) || element.getContainingFile() instanceof JetCodeFragment) { return file is JetFile
ElementAnnotator elementAnnotator = new ElementAnnotator(element, holder);
for (Diagnostic diagnostic : diagnostics.forElement(element)) {
elementAnnotator.registerDiagnosticAnnotations(diagnostic);
}
}
} }
@Override private class ElementAnnotator(private val element: PsiElement, private val holder: AnnotationHolder) {
public boolean isForceHighlightParents(@NotNull PsiFile file) { private var isMarkedWithRedeclaration = false
return file instanceof JetFile;
}
private static class ElementAnnotator { fun registerDiagnosticAnnotations(diagnostic: Diagnostic) {
private final PsiElement element; if (!diagnostic.isValid()) return
private final AnnotationHolder holder;
private boolean isMarkedWithRedeclaration; assert(diagnostic.getPsiElement() == element)
ElementAnnotator(@NotNull PsiElement element, @NotNull AnnotationHolder holder) { val textRanges = diagnostic.getTextRanges()
this.element = element; val factory = diagnostic.getFactory()
this.holder = holder; when (diagnostic.getSeverity()) {
} Severity.ERROR -> {
void registerDiagnosticAnnotations(@NotNull Diagnostic diagnostic) { when (factory) {
if (!diagnostic.isValid()) return; in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS -> {
val referenceExpression = diagnostic.getPsiElement() as JetReferenceExpression
val reference = referenceExpression.getReference()
if (reference is MultiRangeReference) {
for (range in reference.getRanges()) {
val annotation = holder.createErrorAnnotation(range.shiftRight(referenceExpression.getTextOffset()), getDefaultMessage(diagnostic))
setUpAnnotation(diagnostic, annotation, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL)
}
}
else {
for (textRange in textRanges) {
val annotation = holder.createErrorAnnotation(textRange, getDefaultMessage(diagnostic))
setUpAnnotation(diagnostic, annotation, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL)
}
}
}
assert diagnostic.getPsiElement() == element; Errors.ILLEGAL_ESCAPE -> {
for (textRange in textRanges) {
val annotation = holder.createErrorAnnotation(textRange, getDefaultMessage(diagnostic))
annotation.setTooltip(getMessage(diagnostic))
annotation.setTextAttributes(JetHighlightingColors.INVALID_STRING_ESCAPE)
}
}
List<TextRange> textRanges = diagnostic.getTextRanges(); Errors.REDECLARATION -> {
if (diagnostic.getSeverity() == Severity.ERROR) { if (!isMarkedWithRedeclaration) {
if (Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS.contains(diagnostic.getFactory())) { isMarkedWithRedeclaration = true
JetReferenceExpression referenceExpression = (JetReferenceExpression)diagnostic.getPsiElement(); val annotation = holder.createErrorAnnotation(diagnostic.getTextRanges()[0], "")
PsiReference reference = referenceExpression.getReference(); setUpAnnotation(diagnostic, annotation, null)
if (reference instanceof MultiRangeReference) { }
MultiRangeReference mrr = (MultiRangeReference)reference; }
for (TextRange range : mrr.getRanges()) {
Annotation annotation = holder.createErrorAnnotation(range.shiftRight(referenceExpression.getTextOffset()), getDefaultMessage(diagnostic)); else -> {
setUpAnnotation(diagnostic, annotation, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL); for (textRange in textRanges) {
val annotation = holder.createErrorAnnotation(textRange, getDefaultMessage(diagnostic))
setUpAnnotation(diagnostic, annotation, if (factory == Errors.INVISIBLE_REFERENCE)
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL
else
null)
}
} }
} }
else { }
for (TextRange textRange : textRanges) { Severity.WARNING -> {
Annotation annotation = holder.createErrorAnnotation(textRange, getDefaultMessage(diagnostic)); for (textRange in textRanges) {
setUpAnnotation(diagnostic, annotation, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL); val annotation = holder.createWarningAnnotation(textRange, getDefaultMessage(diagnostic))
} setUpAnnotation(diagnostic, annotation, if (factory in Errors.UNUSED_ELEMENT_DIAGNOSTICS)
ProblemHighlightType.LIKE_UNUSED_SYMBOL
else
null)
} }
return;
}
if (diagnostic.getFactory() == Errors.ILLEGAL_ESCAPE) {
for (TextRange textRange : diagnostic.getTextRanges()) {
Annotation annotation = holder.createErrorAnnotation(textRange, getDefaultMessage(diagnostic));
annotation.setTooltip(getMessage(diagnostic));
annotation.setTextAttributes(JetHighlightingColors.INVALID_STRING_ESCAPE);
}
return;
}
if (diagnostic.getFactory() == Errors.REDECLARATION) {
if (!isMarkedWithRedeclaration) {
isMarkedWithRedeclaration = true;
Annotation annotation = holder.createErrorAnnotation(diagnostic.getTextRanges().get(0), "");
setUpAnnotation(diagnostic, annotation, null);
}
return;
}
// Generic annotation
for (TextRange textRange : textRanges) {
Annotation errorAnnotation = holder.createErrorAnnotation(textRange, getDefaultMessage(diagnostic));
setUpAnnotation(diagnostic, errorAnnotation,
diagnostic.getFactory() == Errors.INVISIBLE_REFERENCE
? ProblemHighlightType.LIKE_UNKNOWN_SYMBOL
: null);
}
}
else if (diagnostic.getSeverity() == Severity.WARNING) {
for (TextRange textRange : textRanges) {
Annotation annotation = holder.createWarningAnnotation(textRange, getDefaultMessage(diagnostic));
setUpAnnotation(diagnostic, annotation,
Errors.UNUSED_ELEMENT_DIAGNOSTICS.contains(diagnostic.getFactory())
? ProblemHighlightType.LIKE_UNUSED_SYMBOL
: null);
} }
} }
} }
private static void setUpAnnotation(Diagnostic diagnostic, Annotation annotation, @Nullable ProblemHighlightType highlightType) { private fun setUpAnnotation(diagnostic: Diagnostic, annotation: Annotation, highlightType: ProblemHighlightType?) {
annotation.setTooltip(getMessage(diagnostic)); annotation.setTooltip(getMessage(diagnostic))
registerQuickFix(annotation, diagnostic); registerQuickFix(annotation, diagnostic)
if (highlightType != null) { if (highlightType != null) {
annotation.setHighlightType(highlightType); annotation.setHighlightType(highlightType)
} }
} }
/* private fun registerQuickFix(annotation: Annotation, diagnostic: Diagnostic) {
* Add a quick fix if and return modified annotation. val intentionActionsFactories = QuickFixes.getActionsFactories(diagnostic.getFactory())
*/ for (intentionActionsFactory in intentionActionsFactories) {
@Nullable
private static Annotation registerQuickFix(@Nullable Annotation annotation, @NotNull Diagnostic diagnostic) {
if (annotation == null) {
return null;
}
Collection<JetIntentionActionsFactory> intentionActionsFactories = QuickFixes.getActionsFactories(diagnostic.getFactory());
for (JetIntentionActionsFactory intentionActionsFactory : intentionActionsFactories) {
if (intentionActionsFactory != null) { if (intentionActionsFactory != null) {
for (IntentionAction action: intentionActionsFactory.createActions(diagnostic)) { for (action in intentionActionsFactory.createActions(diagnostic)) {
annotation.registerFix(action); annotation.registerFix(action)
} }
} }
} }
Collection<IntentionAction> actions = QuickFixes.getActions(diagnostic.getFactory()); val actions = QuickFixes.getActions(diagnostic.getFactory())
for (IntentionAction action : actions) { for (action in actions) {
annotation.registerFix(action); annotation.registerFix(action)
} }
// Making warnings suppressable // Making warnings suppressable
if (diagnostic.getSeverity() == Severity.WARNING) { if (diagnostic.getSeverity() == Severity.WARNING) {
annotation.setProblemGroup(new KotlinSuppressableWarningProblemGroup(diagnostic.getFactory())); annotation.setProblemGroup(KotlinSuppressableWarningProblemGroup(diagnostic.getFactory()))
List<Annotation.QuickFixInfo> fixes = annotation.getQuickFixes(); val fixes = annotation.getQuickFixes()
if (fixes == null || fixes.isEmpty()) { if (fixes == null || fixes.isEmpty()) {
// if there are no quick fixes we need to register an EmptyIntentionAction to enable 'suppress' actions // if there are no quick fixes we need to register an EmptyIntentionAction to enable 'suppress' actions
annotation.registerFix(new EmptyIntentionAction(diagnostic.getFactory().getName())); annotation.registerFix(EmptyIntentionAction(diagnostic.getFactory().getName()))
} }
} }
return annotation;
} }
@NotNull private fun getMessage(diagnostic: Diagnostic): String {
private static String getMessage(@NotNull Diagnostic diagnostic) { var message = IdeErrorMessages.RENDERER.render(diagnostic)
String message = IdeErrorMessages.RENDERER.render(diagnostic); if (KotlinInternalMode.enabled || ApplicationManager.getApplication().isUnitTestMode()) {
if (KotlinInternalMode.OBJECT$.getEnabled() || ApplicationManager.getApplication().isUnitTestMode()) { val factoryName = diagnostic.getFactory().getName()
String factoryName = diagnostic.getFactory().getName();
if (message.startsWith("<html>")) { if (message.startsWith("<html>")) {
message = String.format("<html>[%s] %s", factoryName, message.substring("<html>".length())); message = "<html>[$factoryName] ${message.substring("<html>".length())}"
} else { }
message = String.format("[%s] %s", factoryName, message); else {
message = "[$factoryName] $message"
} }
} }
if (!message.startsWith("<html>")) { if (!message.startsWith("<html>")) {
message = "<html><body>" + XmlStringUtil.escapeString(message) + "</body></html>"; message = "<html><body>${XmlStringUtil.escapeString(message)}</body></html>"
} }
return message; return message
} }
@NotNull private fun getDefaultMessage(diagnostic: Diagnostic): String {
private static String getDefaultMessage(@NotNull Diagnostic diagnostic) { val message = DefaultErrorMessages.RENDERER.render(diagnostic)
String message = DefaultErrorMessages.RENDERER.render(diagnostic); if (KotlinInternalMode.enabled || ApplicationManager.getApplication().isUnitTestMode()) {
if (KotlinInternalMode.OBJECT$.getEnabled() || ApplicationManager.getApplication().isUnitTestMode()) { return "[${diagnostic.getFactory().getName()}] $message"
return String.format("[%s] %s", diagnostic.getFactory().getName(), message); }
return message
}
}
class object {
var namesHighlightingEnabled = true
[TestOnly] set
platformStatic fun highlightName(holder: AnnotationHolder, psiElement: PsiElement, attributesKey: TextAttributesKey) {
if (namesHighlightingEnabled) {
holder.createInfoAnnotation(psiElement, null).setTextAttributes(attributesKey)
}
}
private fun getBeforeAnalysisVisitors(holder: AnnotationHolder) = array(
SoftKeywordsHighlightingVisitor(holder),
LabelsHighlightingVisitor(holder)
)
private fun getAfterAnalysisVisitor(holder: AnnotationHolder, bindingContext: BindingContext) = array(
PropertiesHighlightingVisitor(holder, bindingContext),
FunctionsHighlightingVisitor(holder, bindingContext),
VariablesHighlightingVisitor(holder, bindingContext),
TypeKindHighlightingVisitor(holder, bindingContext),
DeprecatedAnnotationVisitor(holder, bindingContext)
)
platformStatic fun annotateElement(element: PsiElement, holder: AnnotationHolder, diagnostics: Diagnostics) {
if (ProjectRootsUtil.isInProjectSource(element) || element.getContainingFile() is JetCodeFragment) {
val elementAnnotator = ElementAnnotator(element, holder)
for (diagnostic in diagnostics.forElement(element)) {
elementAnnotator.registerDiagnosticAnnotations(diagnostic)
}
} }
return message;
} }
} }
@@ -34,7 +34,7 @@ class TypeKindHighlightingVisitor extends AfterAnalysisHighlightingVisitor {
public void visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression) { public void visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression) {
PsiReference ref = expression.getReference(); PsiReference ref = expression.getReference();
if (ref == null) return; if (ref == null) return;
if (JetPsiChecker.isNamesHighlightingEnabled()) { if (JetPsiChecker.OBJECT$.getNamesHighlightingEnabled()) {
DeclarationDescriptor referenceTarget = bindingContext.get(BindingContext.REFERENCE_TARGET, expression); DeclarationDescriptor referenceTarget = bindingContext.get(BindingContext.REFERENCE_TARGET, expression);
if (referenceTarget instanceof ConstructorDescriptor) { if (referenceTarget instanceof ConstructorDescriptor) {
referenceTarget = referenceTarget.getContainingDeclaration(); referenceTarget = referenceTarget.getContainingDeclaration();
@@ -21,7 +21,6 @@ import com.intellij.spellchecker.inspections.SpellCheckingInspection;
import com.intellij.testFramework.LightProjectDescriptor; import com.intellij.testFramework.LightProjectDescriptor;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.plugin.JetLightCodeInsightFixtureTestCase; import org.jetbrains.jet.plugin.JetLightCodeInsightFixtureTestCase;
import org.jetbrains.jet.plugin.JetLightProjectDescriptor;
import org.jetbrains.jet.plugin.highlighter.JetPsiChecker; import org.jetbrains.jet.plugin.highlighter.JetPsiChecker;
import java.io.File; import java.io.File;
@@ -39,11 +38,11 @@ public abstract class AbstractJetPsiCheckerTest extends JetLightCodeInsightFixtu
//noinspection unchecked //noinspection unchecked
myFixture.enableInspections(SpellCheckingInspection.class); myFixture.enableInspections(SpellCheckingInspection.class);
JetPsiChecker.setNamesHighlightingEnabled(false); JetPsiChecker.OBJECT$.setNamesHighlightingEnabled(false);
checkHighlighting(true, true, false); checkHighlighting(true, true, false);
} }
finally { finally {
JetPsiChecker.setNamesHighlightingEnabled(true); JetPsiChecker.OBJECT$.setNamesHighlightingEnabled(true);
} }
} }