Convert JetLineMakerProvider to Kotlin. Auto-convert.

This commit is contained in:
Evgeny Gerashchenko
2015-04-17 14:37:11 +03:00
parent 09cff44207
commit de9b26ac77
2 changed files with 257 additions and 303 deletions
@@ -1,303 +0,0 @@
/*
* Copyright 2010-2015 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 org.jetbrains.kotlin.idea.highlighter;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.codeHighlighting.Pass;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.LineMarkerProvider;
import com.intellij.codeInsight.daemon.impl.LineMarkerNavigator;
import com.intellij.codeInsight.daemon.impl.MarkerType;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.DumbService;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.util.NullableFunction;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.LightClassUtil;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.descriptors.Modality;
import org.jetbrains.kotlin.idea.highlighter.markers.MarkersPackage;
import org.jetbrains.kotlin.idea.highlighter.markers.ResolveWithParentsResult;
import org.jetbrains.kotlin.idea.highlighter.markers.SuperDeclarationMarkerNavigationHandler;
import org.jetbrains.kotlin.idea.highlighter.markers.SuperDeclarationMarkerTooltip;
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil;
import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JetLineMarkerProvider implements LineMarkerProvider {
public static final Icon OVERRIDING_MARK = AllIcons.Gutter.OverridingMethod;
public static final Icon IMPLEMENTING_MARK = AllIcons.Gutter.ImplementingMethod;
protected static final Icon OVERRIDDEN_MARK = AllIcons.Gutter.OverridenMethod;
protected static final Icon IMPLEMENTED_MARK = AllIcons.Gutter.ImplementedMethod;
private static final MarkerType SUBCLASSED_CLASS = new MarkerType(
new NullableFunction<PsiElement, String>() {
@Override
public String fun(@Nullable PsiElement element) {
PsiClass psiClass = MarkersPackage.getPsiClass(element);
return psiClass != null ? MarkerType.getSubclassedClassTooltip(psiClass) : null;
}
},
new LineMarkerNavigator() {
@Override
public void browse(@Nullable MouseEvent e, @Nullable PsiElement element) {
PsiClass psiClass = MarkersPackage.getPsiClass(element);
if (psiClass != null) {
MarkerType.navigateToSubclassedClass(e, psiClass);
}
}
}
);
private static final MarkerType OVERRIDDEN_FUNCTION = new MarkerType(
new NullableFunction<PsiElement, String>() {
@Override
public String fun(@Nullable PsiElement element) {
PsiMethod psiMethod = MarkersPackage.getPsiMethod(element);
return psiMethod != null ? MarkersPackage.getOverriddenMethodTooltip(psiMethod) : null;
}
},
new LineMarkerNavigator() {
@Override
public void browse(@Nullable MouseEvent e, @Nullable PsiElement element) {
PsiMethod psiMethod = MarkersPackage.getPsiMethod(element);
if (psiMethod != null) {
MarkersPackage.navigateToOverriddenMethod(e, psiMethod);
}
}
}
);
private static final MarkerType OVERRIDDEN_PROPERTY = new MarkerType(
new NullableFunction<PsiElement, String>() {
@Override
public String fun(@Nullable PsiElement element) {
if (element == null) return null;
assert element.getParent() instanceof JetProperty : "This tooltip provider should be placed only on identifies in properties";
JetProperty property = (JetProperty) element.getParent();
return MarkersPackage.getOverriddenPropertyTooltip(property);
}
},
new LineMarkerNavigator() {
@Override
public void browse(@Nullable MouseEvent e, @Nullable PsiElement element) {
if (element == null) return;
assert element.getParent() instanceof JetProperty : "This marker navigator should be placed only on identifies in properties";
JetProperty property = (JetProperty) element.getParent();
MarkersPackage.navigateToPropertyOverriddenDeclarations(e, property);
}
}
);
@Override
public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) {
// all Kotlin markers are added in slow marker pass
return null;
}
private static boolean isImplementsAndNotOverrides(
CallableMemberDescriptor descriptor,
Collection<? extends CallableMemberDescriptor> overriddenMembers
) {
if (descriptor.getModality() == Modality.ABSTRACT) return false;
for (CallableMemberDescriptor function : overriddenMembers) {
if (function.getModality() != Modality.ABSTRACT) return false;
}
return true;
}
@Override
public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) {
if (elements.isEmpty()) return;
PsiElement first = KotlinPackage.first(elements);
if (DumbService.getInstance(first.getProject()).isDumb() ||
!ProjectRootsUtil.isInProjectOrLibSource(elements.get(0))) {
return;
}
Set<JetNamedFunction> functions = Sets.newHashSet();
Set<JetProperty> properties = Sets.newHashSet();
for (PsiElement element : elements) {
ProgressManager.checkCanceled();
if (element instanceof JetClass) {
collectInheritedClassMarker((JetClass) element, result);
}
else if (element instanceof JetNamedFunction) {
JetNamedFunction function = (JetNamedFunction) element;
functions.add(function);
collectSuperDeclarationMarkers(function, result);
}
else if (element instanceof JetProperty) {
JetProperty property = (JetProperty) element;
properties.add(property);
collectSuperDeclarationMarkers(property, result);
}
}
collectOverridingAccessors(functions, result);
collectOverridingPropertiesAccessors(properties, result);
}
private static void collectSuperDeclarationMarkers(JetDeclaration declaration, Collection<LineMarkerInfo> result) {
assert (declaration instanceof JetNamedFunction || declaration instanceof JetProperty);
if (!declaration.hasModifier(JetTokens.OVERRIDE_KEYWORD)) return;
ResolveWithParentsResult resolveWithParents = MarkersPackage.resolveDeclarationWithParents(declaration);
if (resolveWithParents.getOverriddenDescriptors().isEmpty()) return;
// NOTE: Don't store descriptors in line markers because line markers are not deleted while editing other files and this can prevent
// clearing the whole BindingTrace.
LineMarkerInfo<JetDeclaration> marker = new LineMarkerInfo<JetDeclaration>(
declaration,
declaration.getTextOffset(),
isImplementsAndNotOverrides(resolveWithParents.getDescriptor(), resolveWithParents.getOverriddenDescriptors()) ?
IMPLEMENTING_MARK :
OVERRIDING_MARK,
Pass.UPDATE_OVERRIDEN_MARKERS,
SuperDeclarationMarkerTooltip.INSTANCE$,
new SuperDeclarationMarkerNavigationHandler()
);
result.add(marker);
}
private static void collectInheritedClassMarker(JetClass element, Collection<LineMarkerInfo> result) {
boolean isTrait = element.isTrait();
if (!(isTrait || element.hasModifier(JetTokens.OPEN_KEYWORD) || element.hasModifier(JetTokens.ABSTRACT_KEYWORD))) {
return;
}
PsiClass lightClass = LightClassUtil.getPsiClass(element);
if (lightClass == null) return;
PsiClass inheritor = ClassInheritorsSearch.search(lightClass, false).findFirst();
if (inheritor != null) {
PsiElement nameIdentifier = element.getNameIdentifier();
PsiElement anchor = nameIdentifier != null ? nameIdentifier : element;
Icon mark = isTrait ? IMPLEMENTED_MARK : OVERRIDDEN_MARK;
result.add(new LineMarkerInfo<PsiElement>(anchor, anchor.getTextOffset(), mark, Pass.UPDATE_OVERRIDEN_MARKERS,
SUBCLASSED_CLASS.getTooltip(), SUBCLASSED_CLASS.getNavigationHandler()));
}
}
public static boolean isImplemented(JetNamedDeclaration declaration) {
if (declaration.hasModifier(JetTokens.ABSTRACT_KEYWORD)) return true;
PsiElement parent = declaration.getParent();
parent = (parent instanceof JetClassBody) ? parent.getParent() : parent;
if (parent instanceof JetClass) {
return ((JetClass) parent).isTrait() &&
(!(declaration instanceof JetDeclarationWithBody) || !((JetDeclarationWithBody)declaration).hasBody()) &&
(!(declaration instanceof JetWithExpressionInitializer) || !((JetWithExpressionInitializer)declaration).hasInitializer());
}
return false;
}
private static void collectOverridingPropertiesAccessors(Collection<JetProperty> properties, Collection<LineMarkerInfo> result) {
Map<PsiMethod, JetProperty> mappingToJava = Maps.newHashMap();
for (JetProperty property : properties) {
if (PsiUtilPackage.isOverridable(property)) {
LightClassUtil.PropertyAccessorsPsiMethods accessorsPsiMethods = LightClassUtil.getLightClassPropertyMethods(property);
for (PsiMethod psiMethod : accessorsPsiMethods) {
mappingToJava.put(psiMethod, property);
}
}
}
Set<PsiClass> classes = MarkersPackage.collectContainingClasses(mappingToJava.keySet());
for (JetProperty property : MarkersPackage.getOverriddenDeclarations(mappingToJava, classes)) {
ProgressManager.checkCanceled();
PsiElement anchor = property.getNameIdentifier();
if (anchor == null) anchor = property;
LineMarkerInfo info = new LineMarkerInfo<PsiElement>(
anchor, anchor.getTextOffset(),
isImplemented(property) ? IMPLEMENTED_MARK : OVERRIDDEN_MARK,
Pass.UPDATE_OVERRIDEN_MARKERS,
OVERRIDDEN_PROPERTY.getTooltip(),
OVERRIDDEN_PROPERTY.getNavigationHandler(),
GutterIconRenderer.Alignment.RIGHT);
result.add(info);
}
}
private static void collectOverridingAccessors(Collection<JetNamedFunction> functions, Collection<LineMarkerInfo> result) {
Map<PsiMethod, JetNamedFunction> mappingToJava = Maps.newHashMap();
for (JetNamedFunction function : functions) {
if (PsiUtilPackage.isOverridable(function)) {
PsiMethod method = LightClassUtil.getLightClassMethod(function);
if (method != null) {
mappingToJava.put(method, function);
}
}
}
Set<PsiClass> classes = MarkersPackage.collectContainingClasses(mappingToJava.keySet());
for (JetNamedFunction function : MarkersPackage.getOverriddenDeclarations(mappingToJava, classes)) {
ProgressManager.checkCanceled();
PsiElement anchor = function.getNameIdentifier();
if (anchor == null) anchor = function;
LineMarkerInfo info = new LineMarkerInfo<PsiElement>(
anchor, anchor.getTextOffset(),
isImplemented(function) ? IMPLEMENTED_MARK : OVERRIDDEN_MARK,
Pass.UPDATE_OVERRIDEN_MARKERS,
OVERRIDDEN_FUNCTION.getTooltip(), OVERRIDDEN_FUNCTION.getNavigationHandler(),
GutterIconRenderer.Alignment.RIGHT);
result.add(info);
}
}
}
@@ -0,0 +1,257 @@
/*
* Copyright 2010-2015 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 org.jetbrains.kotlin.idea.highlighter
import com.google.common.collect.Maps
import com.google.common.collect.Sets
import com.intellij.codeHighlighting.Pass
import com.intellij.codeInsight.daemon.LineMarkerInfo
import com.intellij.codeInsight.daemon.LineMarkerProvider
import com.intellij.codeInsight.daemon.impl.LineMarkerNavigator
import com.intellij.codeInsight.daemon.impl.MarkerType
import com.intellij.icons.AllIcons
import com.intellij.openapi.editor.markup.GutterIconRenderer
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.DumbService
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.searches.ClassInheritorsSearch
import com.intellij.util.NullableFunction
import kotlin.KotlinPackage
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.highlighter.markers.*
import org.jetbrains.kotlin.idea.highlighter.markers.ResolveWithParentsResult
import org.jetbrains.kotlin.idea.highlighter.markers.SuperDeclarationMarkerNavigationHandler
import org.jetbrains.kotlin.idea.highlighter.markers.SuperDeclarationMarkerTooltip
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import javax.swing.*
import java.awt.event.MouseEvent
public class JetLineMarkerProvider : LineMarkerProvider {
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<PsiElement>? {
// all Kotlin markers are added in slow marker pass
return null
}
override fun collectSlowLineMarkers(elements: List<PsiElement>, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
if (elements.isEmpty()) return
val first = KotlinPackage.first<PsiElement>(elements)
if (DumbService.getInstance(first.getProject()).isDumb() || !ProjectRootsUtil.isInProjectOrLibSource(elements.get(0))) {
return
}
val functions = Sets.newHashSet<JetNamedFunction>()
val properties = Sets.newHashSet<JetProperty>()
for (element in elements) {
ProgressManager.checkCanceled()
if (element is JetClass) {
collectInheritedClassMarker(element, result)
}
else if (element is JetNamedFunction) {
functions.add(element)
collectSuperDeclarationMarkers(element, result)
}
else if (element is JetProperty) {
properties.add(element)
collectSuperDeclarationMarkers(element, result)
}
}
collectOverridingAccessors(functions, result)
collectOverridingPropertiesAccessors(properties, result)
}
companion object {
public val OVERRIDING_MARK: Icon = AllIcons.Gutter.OverridingMethod
public val IMPLEMENTING_MARK: Icon = AllIcons.Gutter.ImplementingMethod
protected val OVERRIDDEN_MARK: Icon = AllIcons.Gutter.OverridenMethod
protected val IMPLEMENTED_MARK: Icon = AllIcons.Gutter.ImplementedMethod
private val SUBCLASSED_CLASS = MarkerType(object : NullableFunction<PsiElement, String> {
override fun `fun`(element: PsiElement?): String? {
val psiClass = getPsiClass(element)
return if (psiClass != null) MarkerType.getSubclassedClassTooltip(psiClass) else null
}
},
object : LineMarkerNavigator() {
override fun browse(e: MouseEvent?, element: PsiElement?) {
val psiClass = getPsiClass(element)
if (psiClass != null) {
MarkerType.navigateToSubclassedClass(e, psiClass)
}
}
})
private val OVERRIDDEN_FUNCTION = MarkerType(object : NullableFunction<PsiElement, String> {
override fun `fun`(element: PsiElement?): String? {
val psiMethod = getPsiMethod(element)
return if (psiMethod != null) getOverriddenMethodTooltip(psiMethod) else null
}
},
object : LineMarkerNavigator() {
override fun browse(e: MouseEvent?, element: PsiElement?) {
val psiMethod = getPsiMethod(element)
if (psiMethod != null) {
navigateToOverriddenMethod(e, psiMethod)
}
}
})
private val OVERRIDDEN_PROPERTY = MarkerType(object : NullableFunction<PsiElement, String> {
override fun `fun`(element: PsiElement?): String? {
if (element == null) return null
assert(element.getParent() is JetProperty, "This tooltip provider should be placed only on identifies in properties")
val property = element.getParent() as JetProperty
return getOverriddenPropertyTooltip(property)
}
},
object : LineMarkerNavigator() {
override fun browse(e: MouseEvent?, element: PsiElement?) {
if (element == null) return
assert(element.getParent() is JetProperty, "This marker navigator should be placed only on identifies in properties")
val property = element.getParent() as JetProperty
navigateToPropertyOverriddenDeclarations(e, property)
}
})
private fun isImplementsAndNotOverrides(descriptor: CallableMemberDescriptor, overriddenMembers: Collection<CallableMemberDescriptor>): Boolean {
if (descriptor.getModality() == Modality.ABSTRACT) return false
for (function in overriddenMembers) {
if (function.getModality() != Modality.ABSTRACT) return false
}
return true
}
private fun collectSuperDeclarationMarkers(declaration: JetDeclaration, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
assert((declaration is JetNamedFunction || declaration is JetProperty))
if (!declaration.hasModifier(JetTokens.OVERRIDE_KEYWORD)) return
val resolveWithParents = resolveDeclarationWithParents(declaration)
if (resolveWithParents.overriddenDescriptors.isEmpty()) return
// NOTE: Don't store descriptors in line markers because line markers are not deleted while editing other files and this can prevent
// clearing the whole BindingTrace.
val marker = LineMarkerInfo(declaration, declaration.getTextOffset(), if (isImplementsAndNotOverrides(resolveWithParents.descriptor, resolveWithParents.overriddenDescriptors))
IMPLEMENTING_MARK
else
OVERRIDING_MARK, Pass.UPDATE_OVERRIDEN_MARKERS, SuperDeclarationMarkerTooltip, SuperDeclarationMarkerNavigationHandler())
result.add(marker)
}
private fun collectInheritedClassMarker(element: JetClass, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
val isTrait = element.isTrait()
if (!(isTrait || element.hasModifier(JetTokens.OPEN_KEYWORD) || element.hasModifier(JetTokens.ABSTRACT_KEYWORD))) {
return
}
val lightClass = LightClassUtil.getPsiClass(element)
if (lightClass == null) return
val inheritor = ClassInheritorsSearch.search(lightClass, false).findFirst()
if (inheritor != null) {
val nameIdentifier = element.getNameIdentifier()
val anchor = nameIdentifier ?: element
val mark = if (isTrait) IMPLEMENTED_MARK else OVERRIDDEN_MARK
result.add(LineMarkerInfo(anchor, anchor.getTextOffset(), mark, Pass.UPDATE_OVERRIDEN_MARKERS, SUBCLASSED_CLASS.getTooltip(), SUBCLASSED_CLASS.getNavigationHandler()))
}
}
public fun isImplemented(declaration: JetNamedDeclaration): Boolean {
if (declaration.hasModifier(JetTokens.ABSTRACT_KEYWORD)) return true
var parent = declaration.getParent()
parent = if ((parent is JetClassBody)) parent.getParent() else parent
if (parent is JetClass) {
return (parent as JetClass).isTrait() && (declaration !is JetDeclarationWithBody || !declaration.hasBody()) && (declaration !is JetWithExpressionInitializer || !declaration.hasInitializer())
}
return false
}
private fun collectOverridingPropertiesAccessors(properties: Collection<JetProperty>, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
val mappingToJava = Maps.newHashMap<PsiMethod, JetProperty>()
for (property in properties) {
if (property.isOverridable()) {
val accessorsPsiMethods = LightClassUtil.getLightClassPropertyMethods(property)
for (psiMethod in accessorsPsiMethods) {
mappingToJava.put(psiMethod, property)
}
}
}
val classes = collectContainingClasses(mappingToJava.keySet())
for (property in getOverriddenDeclarations(mappingToJava, classes)) {
ProgressManager.checkCanceled()
var anchor = property.getNameIdentifier()
if (anchor == null) anchor = property
val info = LineMarkerInfo(anchor, anchor!!.getTextOffset(), if (isImplemented(property)) IMPLEMENTED_MARK else OVERRIDDEN_MARK, Pass.UPDATE_OVERRIDEN_MARKERS, OVERRIDDEN_PROPERTY.getTooltip(), OVERRIDDEN_PROPERTY.getNavigationHandler(), GutterIconRenderer.Alignment.RIGHT)
result.add(info)
}
}
private fun collectOverridingAccessors(functions: Collection<JetNamedFunction>, result: MutableCollection<LineMarkerInfo<PsiElement>>) {
val mappingToJava = Maps.newHashMap<PsiMethod, JetNamedFunction>()
for (function in functions) {
if (function.isOverridable()) {
val method = LightClassUtil.getLightClassMethod(function)
if (method != null) {
mappingToJava.put(method, function)
}
}
}
val classes = collectContainingClasses(mappingToJava.keySet())
for (function in getOverriddenDeclarations(mappingToJava, classes)) {
ProgressManager.checkCanceled()
var anchor = function.getNameIdentifier()
if (anchor == null) anchor = function
val info = LineMarkerInfo(anchor, anchor!!.getTextOffset(), if (isImplemented(function)) IMPLEMENTED_MARK else OVERRIDDEN_MARK, Pass.UPDATE_OVERRIDEN_MARKERS, OVERRIDDEN_FUNCTION.getTooltip(), OVERRIDDEN_FUNCTION.getNavigationHandler(), GutterIconRenderer.Alignment.RIGHT)
result.add(info)
}
}
}
}