diff --git a/idea/resources/META-INF/plugin-common.xml b/idea/resources/META-INF/plugin-common.xml
index cae3aa6386c..c3f461a50fd 100644
--- a/idea/resources/META-INF/plugin-common.xml
+++ b/idea/resources/META-INF/plugin-common.xml
@@ -1689,6 +1689,16 @@
Kotlin
+
+ org.jetbrains.kotlin.idea.intentions.AddUnderscoresToNumericLiteralIntention
+ Kotlin
+
+
+
+ org.jetbrains.kotlin.idea.intentions.RemoveUnderscoresFromNumericLiteralIntention
+ Kotlin
+
+
1_000_000
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/AddUnderscoresToNumericLiteralIntention/before.kt.template b/idea/resources/intentionDescriptions/AddUnderscoresToNumericLiteralIntention/before.kt.template
new file mode 100644
index 00000000000..d5744460143
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddUnderscoresToNumericLiteralIntention/before.kt.template
@@ -0,0 +1 @@
+val oneMillion = 1000000
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/AddUnderscoresToNumericLiteralIntention/description.html b/idea/resources/intentionDescriptions/AddUnderscoresToNumericLiteralIntention/description.html
new file mode 100644
index 00000000000..45e9ff447aa
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddUnderscoresToNumericLiteralIntention/description.html
@@ -0,0 +1,5 @@
+
+
+This intention adds underscores to a numeric literal.
+
+
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/after.kt.template b/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/after.kt.template
new file mode 100644
index 00000000000..d5744460143
--- /dev/null
+++ b/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/after.kt.template
@@ -0,0 +1 @@
+val oneMillion = 1000000
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/before.kt.template b/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/before.kt.template
new file mode 100644
index 00000000000..74eab1fdd65
--- /dev/null
+++ b/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/before.kt.template
@@ -0,0 +1 @@
+val oneMillion = 1_000_000
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/description.html b/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/description.html
new file mode 100644
index 00000000000..5ad629bbfdd
--- /dev/null
+++ b/idea/resources/intentionDescriptions/RemoveUnderscoresFromNumericLiteralIntention/description.html
@@ -0,0 +1,5 @@
+
+
+This intention removes underscores from a numeric literal.
+
+
\ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/UnderscoresInNumericLiteralIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/UnderscoresInNumericLiteralIntention.kt
new file mode 100644
index 00000000000..50505b5473b
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/intentions/UnderscoresInNumericLiteralIntention.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.intentions
+
+import com.intellij.openapi.editor.Editor
+import org.jetbrains.kotlin.psi.KtConstantExpression
+import org.jetbrains.kotlin.psi.KtPsiFactory
+import org.jetbrains.kotlin.psi.stubs.ConstantValueKind
+import org.jetbrains.kotlin.psi.stubs.elements.KtConstantExpressionElementType
+
+class AddUnderscoresToNumericLiteralIntention : SelfTargetingIntention(
+ KtConstantExpression::class.java, "Add underscores"
+) {
+ override fun isApplicableTo(element: KtConstantExpression, caretOffset: Int): Boolean {
+ val text = element.text
+ return element.isNumeric() && !text.hasUnderscore() && text.takeWhile { it.isDigit() }.length > 3
+ }
+
+ override fun applyTo(element: KtConstantExpression, editor: Editor?) {
+ val text = element.text
+ val digits = text.takeWhile { it.isDigit() }
+ element.replace(
+ KtPsiFactory(element).createExpression(
+ digits.reversed().chunked(3).joinToString(separator = "_").reversed() + text.removePrefix(digits)
+ )
+ )
+ }
+}
+
+class RemoveUnderscoresFromNumericLiteralIntention : SelfTargetingIntention(
+ KtConstantExpression::class.java, "Remove underscores"
+) {
+ override fun isApplicableTo(element: KtConstantExpression, caretOffset: Int): Boolean {
+ return element.isNumeric() && element.text.hasUnderscore()
+ }
+
+ override fun applyTo(element: KtConstantExpression, editor: Editor?) {
+ element.replace(KtPsiFactory(element).createExpression(element.text.replace("_", "")))
+ }
+}
+
+private fun KtConstantExpression.isNumeric(): Boolean = elementType in numericConstantKinds
+
+private val numericConstantKinds = listOf(
+ KtConstantExpressionElementType.kindToConstantElementType(ConstantValueKind.INTEGER_CONSTANT),
+ KtConstantExpressionElementType.kindToConstantElementType(ConstantValueKind.FLOAT_CONSTANT)
+)
+
+private fun String.hasUnderscore(): Boolean = indexOf('_') != -1
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/.intention b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/.intention
new file mode 100644
index 00000000000..e9b4ac72cff
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/.intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.intentions.AddUnderscoresToNumericLiteralIntention
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/binaries.kt b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/binaries.kt
new file mode 100644
index 00000000000..26d71777a80
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/binaries.kt
@@ -0,0 +1,2 @@
+// IS_APPLICABLE: false
+val x = 0b11010010011010011001010010010010
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/double.kt b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/double.kt
new file mode 100644
index 00000000000..a289440b3ff
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/double.kt
@@ -0,0 +1 @@
+val x = 12345678.9
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/double.kt.after b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/double.kt.after
new file mode 100644
index 00000000000..244b5ba3025
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/double.kt.after
@@ -0,0 +1 @@
+val x = 12_345_678.9
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/float.kt b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/float.kt
new file mode 100644
index 00000000000..67ef05f5528
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/float.kt
@@ -0,0 +1 @@
+val x = 12345678f
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/float.kt.after b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/float.kt.after
new file mode 100644
index 00000000000..74ca09d04cc
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/float.kt.after
@@ -0,0 +1 @@
+val x = 12_345_678f
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/hexadecimals.kt b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/hexadecimals.kt
new file mode 100644
index 00000000000..ea80bfb95e0
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/hexadecimals.kt
@@ -0,0 +1,2 @@
+// IS_APPLICABLE: false
+val x = 0xFFECDE5E
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int.kt b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int.kt
new file mode 100644
index 00000000000..7bb8856cd47
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int.kt
@@ -0,0 +1 @@
+val x = 1234567890
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int.kt.after b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int.kt.after
new file mode 100644
index 00000000000..703c160c701
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int.kt.after
@@ -0,0 +1 @@
+val x = 1_234_567_890
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int2.kt b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int2.kt
new file mode 100644
index 00000000000..7051c261a53
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int2.kt
@@ -0,0 +1,2 @@
+// IS_APPLICABLE: false
+val x = 123
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/long.kt b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/long.kt
new file mode 100644
index 00000000000..52813154e46
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/long.kt
@@ -0,0 +1 @@
+val x = 1234567890L
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/long.kt.after b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/long.kt.after
new file mode 100644
index 00000000000..661d41950d0
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/long.kt.after
@@ -0,0 +1 @@
+val x = 1_234_567_890L
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/uInt.kt b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/uInt.kt
new file mode 100644
index 00000000000..ea1b1bab72f
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/uInt.kt
@@ -0,0 +1,2 @@
+// WITH_RUNTIME
+val x = 1234567890u
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/uInt.kt.after b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/uInt.kt.after
new file mode 100644
index 00000000000..dfc8485cb58
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/uInt.kt.after
@@ -0,0 +1,2 @@
+// WITH_RUNTIME
+val x = 1_234_567_890u
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/.intention b/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/.intention
new file mode 100644
index 00000000000..e272cba4264
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/.intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.intentions.RemoveUnderscoresFromNumericLiteralIntention
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/int.kt b/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/int.kt
new file mode 100644
index 00000000000..b6b78d33688
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/int.kt
@@ -0,0 +1 @@
+val x = 1_234_567_890
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/int.kt.after b/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/int.kt.after
new file mode 100644
index 00000000000..19e01949429
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/int.kt.after
@@ -0,0 +1 @@
+val x = 1234567890
\ No newline at end of file
diff --git a/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/noUnderscores.kt b/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/noUnderscores.kt
new file mode 100644
index 00000000000..9c4010a4e8e
--- /dev/null
+++ b/idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/noUnderscores.kt
@@ -0,0 +1,2 @@
+// IS_APPLICABLE: false
+val x = 1234567890
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
index 166e564618e..35fab2613a5 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
@@ -16619,6 +16619,95 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
+ @TestMetadata("idea/testData/intentions/underscoresInNumericLiteral")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class UnderscoresInNumericLiteral extends AbstractIntentionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInUnderscoresInNumericLiteral() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/underscoresInNumericLiteral"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class AddUnderscores extends AbstractIntentionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInAddUnderscores() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("binaries.kt")
+ public void testBinaries() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/binaries.kt");
+ }
+
+ @TestMetadata("double.kt")
+ public void testDouble() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/double.kt");
+ }
+
+ @TestMetadata("float.kt")
+ public void testFloat() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/float.kt");
+ }
+
+ @TestMetadata("hexadecimals.kt")
+ public void testHexadecimals() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/hexadecimals.kt");
+ }
+
+ @TestMetadata("int.kt")
+ public void testInt() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int.kt");
+ }
+
+ @TestMetadata("int2.kt")
+ public void testInt2() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/int2.kt");
+ }
+
+ @TestMetadata("long.kt")
+ public void testLong() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/long.kt");
+ }
+
+ @TestMetadata("uInt.kt")
+ public void testUInt() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/addUnderscores/uInt.kt");
+ }
+ }
+
+ @TestMetadata("idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class RemoveUnderscores extends AbstractIntentionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInRemoveUnderscores() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("int.kt")
+ public void testInt() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/int.kt");
+ }
+
+ @TestMetadata("noUnderscores.kt")
+ public void testNoUnderscores() throws Exception {
+ runTest("idea/testData/intentions/underscoresInNumericLiteral/removeUnderscores/noUnderscores.kt");
+ }
+ }
+ }
+
@TestMetadata("idea/testData/intentions/usePropertyAccessSyntax")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)