diff --git a/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/after.kt.template b/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/after.kt.template
new file mode 100644
index 00000000000..6c743b16877
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/after.kt.template
@@ -0,0 +1,3 @@
+for ((i, x) in foo.withIndices()) {
+
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/before.kt.template b/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/before.kt.template
new file mode 100644
index 00000000000..8b1fae6e977
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/before.kt.template
@@ -0,0 +1,3 @@
+for (x in foo) {
+
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/description.html b/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/description.html
new file mode 100644
index 00000000000..2017c1d8723
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddForLoopIndicesIntention/description.html
@@ -0,0 +1,5 @@
+
+
+This intention adds an index variable to a for loop iterating over a collection.
+
+
diff --git a/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/after.kt.template b/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/after.kt.template
new file mode 100644
index 00000000000..8b1fae6e977
--- /dev/null
+++ b/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/after.kt.template
@@ -0,0 +1,3 @@
+for (x in foo) {
+
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/before.kt.template b/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/before.kt.template
new file mode 100644
index 00000000000..6c743b16877
--- /dev/null
+++ b/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/before.kt.template
@@ -0,0 +1,3 @@
+for ((i, x) in foo.withIndices()) {
+
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/description.html b/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/description.html
new file mode 100644
index 00000000000..dfdfe1e2a0c
--- /dev/null
+++ b/idea/resources/intentionDescriptions/RemoveForLoopIndicesIntention/description.html
@@ -0,0 +1,5 @@
+
+
+This intention removes the index variable from a for loop iterating over a collection.
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index d2207a7ac84..fa8065d6150 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -793,6 +793,21 @@
Kotlin
+
+ org.jetbrains.kotlin.idea.intentions.AddForLoopIndicesIntention
+ Kotlin
+ S
+
+
+ org.jetbrains.kotlin.idea.intentions.RemoveForLoopIndicesIntention
+ Kotlin
+ S
+
+
+ org.jetbrains.jet.plugin.intentions.SwapBinaryExpression
+ Kotlin
+
+
org.jetbrains.kotlin.idea.intentions.SwapBinaryExpressionIntentionKotlin
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddForLoopIndicesIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddForLoopIndicesIntention.kt
new file mode 100644
index 00000000000..5030329a8d2
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AddForLoopIndicesIntention.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2010-2014 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.intentions
+
+import com.intellij.openapi.editor.Editor
+import org.jetbrains.jet.lang.psi.JetForExpression
+import org.jetbrains.jet.lang.psi.JetPsiFactory
+import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression
+import com.intellij.psi.util.PsiTreeUtil
+import com.intellij.psi.PsiDocumentManager
+import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
+import org.jetbrains.jet.lang.resolve.BindingContext
+import org.jetbrains.jet.lang.psi.JetParenthesizedExpression
+import com.intellij.codeInsight.template.TemplateBuilderImpl
+import com.intellij.codeInsight.template.impl.TemplateManagerImpl
+import org.jetbrains.jet.lang.resolve.DescriptorUtils
+import org.jetbrains.jet.analyzer.analyzeInContext
+import org.jetbrains.jet.lang.psi.JetCallExpression
+
+public class AddForLoopIndicesIntention : JetSelfTargetingIntention(
+ "add.for.loop.indices", javaClass()) {
+ override fun applyTo(element: JetForExpression, editor: Editor) {
+ val loopRange = element.getLoopRange()!!
+ val newRangeText = "${loopRange.getText()}.withIndices()"
+ val newRange = JetPsiFactory.createExpression(editor.getProject(), newRangeText)
+
+ //Roundabout way to create new multiparameter element so as not to incorrectly trigger syntax error highlighting
+ val loopParameter = element.getLoopParameter()!!
+ val parenthesizedParam = JetPsiFactory.createExpression(editor.getProject(), "(index)") as JetParenthesizedExpression
+ val indexElement = parenthesizedParam.getExpression()!!
+ val comma = JetPsiFactory.createComma(editor.getProject())
+ val newParamElement = JetPsiFactory.createExpression(editor.getProject(), " ${loopParameter.getText()}")
+ parenthesizedParam.addAfter(newParamElement, indexElement)
+ parenthesizedParam.addAfter(comma, indexElement)
+
+ loopParameter.replace(parenthesizedParam)
+ loopRange.replace(newRange)
+
+ val multiParameter = PsiTreeUtil.findChildOfType(element, indexElement.javaClass)!!
+
+ editor.getCaretModel().moveToOffset(multiParameter.getTextOffset())
+ val templateBuilder = TemplateBuilderImpl(multiParameter)
+ templateBuilder.replaceElement(multiParameter, "index")
+ val manager = TemplateManagerImpl(editor.getProject())
+ PsiDocumentManager.getInstance(editor.getProject()!!).doPostponedOperationsAndUnblockDocument(editor.getDocument())
+ manager.startTemplate(editor, templateBuilder.buildInlineTemplate()!!)
+ }
+
+ override fun isApplicableTo(element: JetForExpression): Boolean {
+ if (element.getLoopParameter() == null) return false
+ val range = element.getLoopRange() ?: return false
+ if (range is JetDotQualifiedExpression) {
+ val selector = range.getSelectorExpression() ?: return true
+ if (selector.getText() == "withIndices()") return false
+ }
+
+ val potentialExpression = JetPsiFactory.createExpression(element.getProject(),
+ "${range.getText()}.withIndices()") as JetDotQualifiedExpression
+
+ val bindingContext = AnalyzerFacadeWithCache.getContextForElement(element)
+ val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, element] ?: return false
+ val functionSelector = potentialExpression.getSelectorExpression() as JetCallExpression
+ val updatedContext = potentialExpression.analyzeInContext(scope)
+ val callScope = updatedContext[BindingContext.RESOLVED_CALL, functionSelector.getCalleeExpression()!!] ?: return false
+ val callFqName = DescriptorUtils.getFqNameSafe(callScope.getCandidateDescriptor())
+ return callFqName.toString() == "kotlin.withIndices"
+ }
+
+}
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveForLoopIndicesIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveForLoopIndicesIntention.kt
new file mode 100644
index 00000000000..638781790ef
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/intentions/RemoveForLoopIndicesIntention.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2010-2014 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.intentions
+
+import org.jetbrains.jet.lang.psi.JetForExpression
+import com.intellij.openapi.editor.Editor
+import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression
+import org.jetbrains.jet.lang.psi.JetPsiUtil
+import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
+import com.intellij.find.FindManager
+import com.intellij.find.impl.FindManagerImpl
+import com.intellij.usageView.UsageInfo
+import org.jetbrains.jet.plugin.findUsages.KotlinPropertyFindUsagesOptions
+import org.jetbrains.jet.lang.resolve.BindingContext
+import org.jetbrains.jet.lang.psi.JetCallExpression
+import org.jetbrains.jet.lang.resolve.DescriptorUtils
+
+public class RemoveForLoopIndicesIntention : JetSelfTargetingIntention(
+ "remove.for.loop.indices", javaClass()) {
+ override fun applyTo(element: JetForExpression, editor: Editor) {
+ val parameter = element.getMultiParameter()!!
+ val range = element.getLoopRange() as JetDotQualifiedExpression
+ val parameters = parameter.getEntries()
+ if (parameters.size() == 2) {
+ parameter.replace(parameters[1])
+ }
+ else {
+ JetPsiUtil.deleteElementWithDelimiters(parameters[0])
+ }
+
+ range.replace(range.getReceiverExpression())
+ }
+
+ override fun isApplicableTo(element: JetForExpression): Boolean {
+ val multiParameter = element.getMultiParameter() ?: return false
+ val range = element.getLoopRange() as? JetDotQualifiedExpression ?: return false
+ val selector = range.getSelectorExpression() as? JetCallExpression ?: return false
+
+ if (!selector.textMatches("withIndices()")) return false
+
+ val bindingContext = AnalyzerFacadeWithCache.getContextForElement(element)
+ val callResolution = bindingContext[BindingContext.RESOLVED_CALL, selector.getCalleeExpression()!!] ?: return false
+ val fqName = DescriptorUtils.getFqNameSafe(callResolution.getCandidateDescriptor())
+ if (fqName.toString() != "kotlin.withIndices") return false
+
+ val indexVar = multiParameter.getEntries()[0]
+ val findManager = FindManager.getInstance(element.getProject()) as FindManagerImpl
+ val findHandler = findManager.getFindUsagesManager().getFindUsagesHandler(indexVar, false) ?: return false
+ val options = KotlinPropertyFindUsagesOptions(element.getProject())
+ var usageCount = 0
+ val processorLambda: (UsageInfo?) -> Boolean = { t -> usageCount++; false }
+ findHandler.processElementUsages(indexVar, processorLambda, options)
+ return usageCount == 0
+ }
+
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/.intention b/idea/testData/intentions/addForLoopIndicesIntention/.intention
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/inapplicableExistingIndices.kt b/idea/testData/intentions/addForLoopIndicesIntention/inapplicableExistingIndices.kt
new file mode 100644
index 00000000000..b9b2b899071
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/inapplicableExistingIndices.kt
@@ -0,0 +1,7 @@
+//IS_APPLICABLE: FALSE
+// WITH_RUNTIME
+fun b(c: List) {
+ for ((indexVariable, d) in c.withIndices()) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/inapplicableOverridenFunction.kt b/idea/testData/intentions/addForLoopIndicesIntention/inapplicableOverridenFunction.kt
new file mode 100644
index 00000000000..c16ceeb8a97
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/inapplicableOverridenFunction.kt
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: FALSE
+fun String.withIndices(): Int = 42
+
+fun foo(s: String) {
+ for (a in s) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/inapplicableSyntaxError.kt b/idea/testData/intentions/addForLoopIndicesIntention/inapplicableSyntaxError.kt
new file mode 100644
index 00000000000..130fb1e80ba
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/inapplicableSyntaxError.kt
@@ -0,0 +1,7 @@
+//IS_APPLICABLE: FALSE
+//ERROR: Unresolved reference: b
+fun foo() {
+ for (a in b) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/inapplicableUnorderedCollection.kt b/idea/testData/intentions/addForLoopIndicesIntention/inapplicableUnorderedCollection.kt
new file mode 100644
index 00000000000..42c9ecb7ee7
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/inapplicableUnorderedCollection.kt
@@ -0,0 +1,7 @@
+// IS_APPLICABLE: FALSE
+// WITH_RUNTIME
+fun foo(bar: Map) {
+ for (a in bar) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/intArray.kt b/idea/testData/intentions/addForLoopIndicesIntention/intArray.kt
new file mode 100644
index 00000000000..40ab0ca1785
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/intArray.kt
@@ -0,0 +1,6 @@
+//WITH_RUNTIME
+fun foo(bar: IntArray) {
+ for (a in bar) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/intArray.kt.after b/idea/testData/intentions/addForLoopIndicesIntention/intArray.kt.after
new file mode 100644
index 00000000000..317eb113f87
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/intArray.kt.after
@@ -0,0 +1,6 @@
+//WITH_RUNTIME
+fun foo(bar: IntArray) {
+ for ((index, a) in bar.withIndices()) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/iterable.kt b/idea/testData/intentions/addForLoopIndicesIntention/iterable.kt
new file mode 100644
index 00000000000..aca19b4b400
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/iterable.kt
@@ -0,0 +1,6 @@
+//WITH_RUNTIME
+fun foo(bar: Iterable) {
+ for (a in bar) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/iterable.kt.after b/idea/testData/intentions/addForLoopIndicesIntention/iterable.kt.after
new file mode 100644
index 00000000000..4351c3856a9
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/iterable.kt.after
@@ -0,0 +1,6 @@
+//WITH_RUNTIME
+fun foo(bar: Iterable) {
+ for ((index, a) in bar.withIndices()) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/listWithType.kt b/idea/testData/intentions/addForLoopIndicesIntention/listWithType.kt
new file mode 100644
index 00000000000..0cadfa4e63b
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/listWithType.kt
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+fun a() {
+ val b = listOf(1,2,3,4,5)
+ for (c : Int in b) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/listWithType.kt.after b/idea/testData/intentions/addForLoopIndicesIntention/listWithType.kt.after
new file mode 100644
index 00000000000..633543f3ffe
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/listWithType.kt.after
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+fun a() {
+ val b = listOf(1,2,3,4,5)
+ for ((index, c : Int) in b.withIndices()) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addForLoopIndicesIntention/objectArray.kt b/idea/testData/intentions/addForLoopIndicesIntention/objectArray.kt
new file mode 100644
index 00000000000..32c665ac8a3
--- /dev/null
+++ b/idea/testData/intentions/addForLoopIndicesIntention/objectArray.kt
@@ -0,0 +1,6 @@
+//WITH_RUNTIME
+fun foo(bar: Array