Refine highlighting of '@'
- Do not highlight '@' as separate token. - Highlight '@' the same way as element where it's located: annotation, label or modifier. - Extend text range when positioning unresolved reference in annotation with '@'. Note that currently '@' is collapsed with modifiers tokens
This commit is contained in:
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.JetNodeTypes
|
||||
import org.jetbrains.kotlin.lexer.JetKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.JetTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getCalleeHighlightingRange
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.utils.sure
|
||||
import kotlin.platform.platformStatic
|
||||
@@ -226,7 +227,7 @@ public object PositioningStrategies {
|
||||
return ranges
|
||||
}
|
||||
}
|
||||
return listOf(element.getTextRange())
|
||||
return listOf(element.getCalleeHighlightingRange())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
package org.jetbrains.kotlin.psi;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.JetNodeTypes;
|
||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.psi.stubs.KotlinAnnotationEntryStub;
|
||||
import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes;
|
||||
|
||||
@@ -101,4 +103,8 @@ public class JetAnnotationEntry extends JetElementImplStub<KotlinAnnotationEntry
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement getAtSymbol() {
|
||||
return findChildByType(JetTokens.AT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,3 +570,15 @@ private fun JetAnnotationsContainer.collectAnnotationEntriesFromPsi() =
|
||||
else -> emptyList<JetAnnotationEntry>()
|
||||
}
|
||||
}
|
||||
|
||||
public fun JetElement.getCalleeHighlightingRange(): TextRange {
|
||||
val annotationEntry: JetAnnotationEntry =
|
||||
PsiTreeUtil.getParentOfType<JetAnnotationEntry>(
|
||||
this, javaClass<JetAnnotationEntry>(), /* strict = */false, javaClass<JetValueArgumentList>()
|
||||
) ?: return getTextRange()
|
||||
|
||||
val startOffset = annotationEntry.getAtSymbol()?.getTextRange()?.getStartOffset()
|
||||
?: annotationEntry.getCalleeExpression().getTextRange().getStartOffset()
|
||||
|
||||
return TextRange(startOffset, annotationEntry.getCalleeExpression().getTextRange().getEndOffset())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<!UNRESOLVED_REFERENCE!>@Ann<!> class A
|
||||
<!UNRESOLVED_REFERENCE!>Ann<!> class B
|
||||
<!UNRESOLVED_REFERENCE!>@Ann<!>(1) class C
|
||||
@@ -0,0 +1,22 @@
|
||||
package
|
||||
|
||||
[ERROR : Ann]() internal final class A {
|
||||
public constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
[ERROR : Ann]() internal final class B {
|
||||
public constructor B()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
|
||||
[ERROR : Ann]() internal final class C {
|
||||
public constructor C()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
@@ -801,6 +801,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unresolvedReferenceRange.kt")
|
||||
public void testUnresolvedReferenceRange() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/unresolvedReferenceRange.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("WrongAnnotationArgsOnObject.kt")
|
||||
public void testWrongAnnotationArgsOnObject() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/annotations/WrongAnnotationArgsOnObject.kt");
|
||||
|
||||
@@ -52,7 +52,6 @@ public class JetHighlighter extends SyntaxHighlighterBase {
|
||||
fillMap(keys1, JetTokens.KEYWORDS, JetHighlightingColors.KEYWORD);
|
||||
|
||||
keys1.put(JetTokens.AS_SAFE, JetHighlightingColors.KEYWORD);
|
||||
keys1.put(JetTokens.AT, JetHighlightingColors.LABEL);
|
||||
keys1.put(JetTokens.INTEGER_LITERAL, JetHighlightingColors.NUMBER);
|
||||
keys1.put(JetTokens.FLOAT_LITERAL, JetHighlightingColors.NUMBER);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.openapi.progress.ProcessCanceledException
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.MultiRangeReference
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
@@ -236,6 +237,12 @@ public open class JetPsiChecker : Annotator, HighlightRangeExtension {
|
||||
}
|
||||
}
|
||||
|
||||
platformStatic fun highlightName(holder: AnnotationHolder, textRange: TextRange, attributesKey: TextAttributesKey) {
|
||||
if (namesHighlightingEnabled) {
|
||||
holder.createInfoAnnotation(textRange, null).setTextAttributes(attributesKey)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getBeforeAnalysisVisitors(holder: AnnotationHolder) = array(
|
||||
SoftKeywordsHighlightingVisitor(holder),
|
||||
LabelsHighlightingVisitor(holder),
|
||||
|
||||
+14
-1
@@ -18,11 +18,13 @@ package org.jetbrains.kotlin.idea.highlighter;
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
|
||||
class TypeKindHighlightingVisitor extends AfterAnalysisHighlightingVisitor {
|
||||
@@ -41,7 +43,13 @@ class TypeKindHighlightingVisitor extends AfterAnalysisHighlightingVisitor {
|
||||
}
|
||||
|
||||
if (referenceTarget instanceof ClassDescriptor) {
|
||||
highlightName(expression, textAttributesKeyForClass((ClassDescriptor) referenceTarget));
|
||||
TextAttributesKey textAttributesKey = textAttributesKeyForClass((ClassDescriptor) referenceTarget);
|
||||
if (textAttributesKey == JetHighlightingColors.ANNOTATION) {
|
||||
highlightAnnotation(expression);
|
||||
}
|
||||
else {
|
||||
highlightName(expression, textAttributesKey);
|
||||
}
|
||||
}
|
||||
else if (referenceTarget instanceof TypeParameterDescriptor) {
|
||||
highlightName(expression, JetHighlightingColors.TYPE_PARAMETER);
|
||||
@@ -49,6 +57,11 @@ class TypeKindHighlightingVisitor extends AfterAnalysisHighlightingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
private void highlightAnnotation(@NotNull JetSimpleNameExpression expression) {
|
||||
TextRange toHighlight = PsiUtilPackage.getCalleeHighlightingRange(expression);
|
||||
JetPsiChecker.highlightName(holder, toHighlight, JetHighlightingColors.ANNOTATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitObjectDeclarationName(@NotNull JetObjectDeclarationName declaration) {
|
||||
PsiElement nameIdentifier = declaration.getNameIdentifier();
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<info descr="null">annotation</info> class <info descr="null">Ann</info>
|
||||
|
||||
<info descr="null" textAttributesKey="KOTLIN_ANNOTATION">Ann</info> class <info descr="null">A1</info>
|
||||
<info descr="null" textAttributesKey="KOTLIN_ANNOTATION">@Ann</info> class <info descr="null">A2</info>
|
||||
|
||||
fun <info descr="null">bar</info>(<info descr="null">block</info>: () -> <info descr="null">Int</info>) = <info descr="null"><info descr="null">block</info></info>()
|
||||
|
||||
<info descr="null" textAttributesKey="KOTLIN_BUILTIN_ANNOTATION">@private</info>
|
||||
fun <info descr="null">foo</info>() {
|
||||
1 + [<info descr="null" textAttributesKey="KOTLIN_ANNOTATION">Ann</info>] 2
|
||||
|
||||
<info descr="null" textAttributesKey="KOTLIN_ANNOTATION">@Ann</info> 3 + 4
|
||||
|
||||
<info descr="null"><info descr="null">bar</info></info> <info descr="null" textAttributesKey="KOTLIN_ANNOTATION">@Ann</info> { 1 }
|
||||
|
||||
<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: Err" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">@Err</error>
|
||||
<warning descr="[UNUSED_EXPRESSION] The expression is unused" textAttributesKey="WARNING_ATTRIBUTES">5</warning>
|
||||
}
|
||||
|
||||
<error descr="[UNRESOLVED_REFERENCE] Unresolved reference: Err" textAttributesKey="WRONG_REFERENCES_ATTRIBUTES">@Err</error> class <info descr="null" textAttributesKey="KOTLIN_CLASS">Err1</info>
|
||||
|
||||
class <info descr="null">NotAnn</info>
|
||||
<error descr="[NOT_AN_ANNOTATION_CLASS] 'NotAnn' is not an annotation class" textAttributesKey="ERRORS_ATTRIBUTES">@NotAnn</error>
|
||||
class <info descr="null">C</info>
|
||||
@@ -0,0 +1,17 @@
|
||||
fun <info descr="null">bar</info>(<info descr="null">block</info>: () -> <info descr="null">Int</info>) = <info descr="null"><info descr="null">block</info></info>()
|
||||
|
||||
fun <info descr="null">foo</info>(): <info descr="null">Int</info> {
|
||||
<info descr="null"><info descr="null">bar</info></info> <info descr="null" textAttributesKey="KOTLIN_LABEL">label@</info> {
|
||||
return<info descr="null" textAttributesKey="KOTLIN_LABEL">@label</info> 2
|
||||
}
|
||||
|
||||
<info descr="null" textAttributesKey="KOTLIN_LABEL">loop@</info> for (<info descr="null">i</info> in 1..100) {
|
||||
break<info descr="null" textAttributesKey="KOTLIN_LABEL">@loop</info>
|
||||
}
|
||||
|
||||
<info descr="null" textAttributesKey="KOTLIN_LABEL">loop2@</info> for (<info descr="null">i</info> in 1..100) {
|
||||
break<error descr="There should be no space or comments before '@' in label reference"> </error><info descr="null" textAttributesKey="KOTLIN_LABEL">@loop2</info>
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
@@ -35,6 +35,12 @@ public class HighlightingTestGenerated extends AbstractHighlightingTest {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/highlighter"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("Annotations.kt")
|
||||
public void testAnnotations() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/highlighter/Annotations.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Dynamic.kt")
|
||||
public void testDynamic() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/highlighter/Dynamic.kt");
|
||||
@@ -65,6 +71,12 @@ public class HighlightingTestGenerated extends AbstractHighlightingTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Labels.kt")
|
||||
public void testLabels() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/highlighter/Labels.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("Object.kt")
|
||||
public void testObject() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/highlighter/Object.kt");
|
||||
|
||||
Reference in New Issue
Block a user