diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinIcons.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinIcons.java index a7c8fbddb84..0971330922c 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinIcons.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/KotlinIcons.java @@ -45,5 +45,7 @@ public interface KotlinIcons { Icon CLASS_INITIALIZER = IconLoader.getIcon("/org/jetbrains/kotlin/idea/icons/classInitializerKotlin.png"); Icon TYPE_ALIAS = IconLoader.getIcon("/org/jetbrains/kotlin/idea/icons/typeAlias.png"); + Icon SUSPEND_CALL = IconLoader.getIcon("/org/jetbrains/kotlin/idea/icons/suspendCall.png"); + Icon LAUNCH = IconLoader.getIcon("/org/jetbrains/kotlin/idea/icons/kotlin_launch_configuration.png"); } diff --git a/idea/resources/org/jetbrains/kotlin/idea/icons/suspendCall.png b/idea/resources/org/jetbrains/kotlin/idea/icons/suspendCall.png new file mode 100644 index 00000000000..ce9099594aa Binary files /dev/null and b/idea/resources/org/jetbrains/kotlin/idea/icons/suspendCall.png differ diff --git a/idea/resources/org/jetbrains/kotlin/idea/icons/suspendCall@2x.png b/idea/resources/org/jetbrains/kotlin/idea/icons/suspendCall@2x.png new file mode 100644 index 00000000000..01b1b94c449 Binary files /dev/null and b/idea/resources/org/jetbrains/kotlin/idea/icons/suspendCall@2x.png differ diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index a3830505d06..63608a8caf1 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -545,6 +545,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt new file mode 100644 index 00000000000..dbdf69629b8 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinSuspendCallLineMarkerProvider.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2016 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.intellij.codeHighlighting.Pass +import com.intellij.codeInsight.daemon.LineMarkerInfo +import com.intellij.codeInsight.daemon.LineMarkerProvider +import com.intellij.openapi.editor.markup.GutterIconRenderer +import com.intellij.openapi.progress.ProgressManager +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.idea.KotlinIcons +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.refactoring.getLineNumber +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + +class KotlinSuspendCallLineMarkerProvider : LineMarkerProvider { + private class SuspendCallMarkerInfo(callElement: KtElement) : LineMarkerInfo( + callElement, + callElement.textRange, + KotlinIcons.SUSPEND_CALL, + Pass.UPDATE_OVERRIDDEN_MARKERS, + { "Suspend function call" }, + null, + GutterIconRenderer.Alignment.RIGHT + ) { + override fun createGutterRenderer(): GutterIconRenderer? { + return object : LineMarkerInfo.LineMarkerGutterIconRenderer(this) { + override fun getClickAction() = null + } + } + } + + override fun getLineMarkerInfo(element: PsiElement) = null + + override fun collectSlowLineMarkers( + elements: MutableList, + result: MutableCollection> + ) { + val markedLineNumbers = HashSet() + + for (element in elements) { + ProgressManager.checkCanceled() + + if (element !is KtElement) continue + + val lineNumber = element.getLineNumber() + if (lineNumber in markedLineNumbers) continue + + val resolvedCall = element.getResolvedCall(element.analyze(BodyResolveMode.PARTIAL)) ?: continue + val calleeDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor ?: continue + if (!calleeDescriptor.isSuspend) continue + + markedLineNumbers += lineNumber + result += SuspendCallMarkerInfo(element) + } + } +} diff --git a/idea/testData/codeInsight/lineMarker/suspendCall/suspendCall.kt b/idea/testData/codeInsight/lineMarker/suspendCall/suspendCall.kt new file mode 100644 index 00000000000..a3004d891e6 --- /dev/null +++ b/idea/testData/codeInsight/lineMarker/suspendCall/suspendCall.kt @@ -0,0 +1,7 @@ +suspend fun fff() { + +} + +suspend fun ggg() { + return fff() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/LineMarkersTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/LineMarkersTestGenerated.java index fa1a19cf9f9..9aaa3057393 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/LineMarkersTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/LineMarkersTestGenerated.java @@ -320,4 +320,19 @@ public class LineMarkersTestGenerated extends AbstractLineMarkersTest { doTest(fileName); } } + + @TestMetadata("idea/testData/codeInsight/lineMarker/suspendCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendCall extends AbstractLineMarkersTest { + public void testAllFilesPresentInSuspendCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/codeInsight/lineMarker/suspendCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("suspendCall.kt") + public void testSuspendCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/codeInsight/lineMarker/suspendCall/suspendCall.kt"); + doTest(fileName); + } + } }