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