diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/InsertLiteralUnderscoresAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/InsertLiteralUnderscoresAction.java new file mode 100644 index 000000000000..40762fde4f14 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/InsertLiteralUnderscoresAction.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2011 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 com.intellij.codeInsight.intention.impl; + +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.text.LiteralFormatUtil; +import org.jetbrains.annotations.NotNull; + +public class InsertLiteralUnderscoresAction extends PsiElementBaseIntentionAction { + @Override + public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) { + if (!PsiUtil.isLanguageLevel7OrHigher(element)) return false; + + final PsiLiteralExpression literalExpression = PsiTreeUtil.getParentOfType(element, PsiLiteralExpression.class, false); + if (literalExpression == null) return false; + + final PsiType type = literalExpression.getType(); + if (!PsiType.INT.equals(type) && !PsiType.LONG.equals(type) && + !PsiType.FLOAT.equals(type) && !PsiType.DOUBLE.equals(type)) return false; + + final String text = literalExpression.getText(); + return text != null && !text.contains("_"); + } + + @Override + public void invoke(final Project project, final Editor editor, final PsiElement element) throws IncorrectOperationException { + final PsiLiteralExpression literalExpression = PsiTreeUtil.getParentOfType(element, PsiLiteralExpression.class, false); + if (literalExpression == null) return; + + final String text = literalExpression.getText(); + final PsiType type = literalExpression.getType(); + final String converted = LiteralFormatUtil.format(text, type); + if (converted.length() == text.length()) return; + + final PsiExpression replacement = JavaPsiFacade.getElementFactory(project).createExpressionFromText(converted, null); + literalExpression.replace(replacement); + } + + @NotNull + @Override + public String getFamilyName() { + return CodeInsightBundle.message("intention.underscores.in.literals.family"); + } + + @NotNull + @Override + public String getText() { + return CodeInsightBundle.message("intention.insert.literal.underscores"); + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/RemoveLiteralUnderscoresAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/RemoveLiteralUnderscoresAction.java new file mode 100644 index 000000000000..4207625a54ca --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/RemoveLiteralUnderscoresAction.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2011 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 com.intellij.codeInsight.intention.impl; + +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.text.LiteralFormatUtil; +import org.jetbrains.annotations.NotNull; + +public class RemoveLiteralUnderscoresAction extends PsiElementBaseIntentionAction { + @Override + public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) { + final PsiLiteralExpression literalExpression = PsiTreeUtil.getParentOfType(element, PsiLiteralExpression.class, false); + if (literalExpression == null) return false; + + final PsiType type = literalExpression.getType(); + if (!PsiType.INT.equals(type) && !PsiType.LONG.equals(type) && + !PsiType.FLOAT.equals(type) && !PsiType.DOUBLE.equals(type)) return false; + + final String text = literalExpression.getText(); + return text != null && text.contains("_"); + } + + @Override + public void invoke(final Project project, final Editor editor, final PsiElement element) throws IncorrectOperationException { + final PsiLiteralExpression literalExpression = PsiTreeUtil.getParentOfType(element, PsiLiteralExpression.class, false); + if (literalExpression == null) return; + + final String text = literalExpression.getText(); + final String converted = LiteralFormatUtil.removeUnderscores(text); + if (converted.length() == text.length()) return; + + final PsiExpression replacement = JavaPsiFacade.getElementFactory(project).createExpressionFromText(converted, null); + literalExpression.replace(replacement); + } + + @NotNull + @Override + public String getFamilyName() { + return CodeInsightBundle.message("intention.underscores.in.literals.family"); + } + + @NotNull + @Override + public String getText() { + return CodeInsightBundle.message("intention.remove.literal.underscores"); + } +} diff --git a/java/java-impl/src/com/intellij/util/text/LiteralFormatUtil.java b/java/java-impl/src/com/intellij/util/text/LiteralFormatUtil.java index 7eb898454596..4141d2098358 100644 --- a/java/java-impl/src/com/intellij/util/text/LiteralFormatUtil.java +++ b/java/java-impl/src/com/intellij/util/text/LiteralFormatUtil.java @@ -15,14 +15,119 @@ */ package com.intellij.util.text; +import com.intellij.openapi.util.text.CharFilter; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiType; +import com.intellij.util.StringBuilderSpinAllocator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class LiteralFormatUtil { + private static final CharFilter UNDERSCORES_FILTER = new CharFilter() { + @Override + public boolean accept(final char ch) { + return ch != '_'; + } + }; + private LiteralFormatUtil() { } @NotNull public static String removeUnderscores(@NotNull final String text) { - return StringUtil.cleanString(text, "_"); + return StringUtil.strip(text, UNDERSCORES_FILTER); + } + + @NotNull + public static String format(@NotNull final String original, @Nullable final PsiType type) { + final boolean isFP = PsiType.FLOAT.equals(type) || PsiType.DOUBLE.equals(type); + + String text = original; + String prefix = ""; + String suffix = ""; + int groupSize = 3; // dec, oct + + if (text.startsWith("0x") || text.startsWith("0X") || + text.startsWith("0b") || text.startsWith("0B")) { + prefix = text.substring(0, 2); + text = text.substring(2); + groupSize = 4; // hex, bin + } + + if (text.length() == 0) return original; + + final char last = text.charAt(text.length() - 1); + if (StringUtil.containsChar("Ll", last) || + (isFP && StringUtil.containsChar("FfDd", last))) { + final int pos = text.length() - 1; + suffix = text.substring(pos); + text = text.substring(0, pos); + } + + if (text.length() == 0) return original; + + boolean hasPoint = false; + String fractional = ""; + String exponentMark = ""; + String exponent = ""; + if (isFP) { + int pos = StringUtil.indexOfAny(text, ("0x".equals(prefix) || "0X".equals(prefix) ? "Pp" : "Ee")); + if (pos >= 0) { + int pos2 = Math.max(StringUtil.indexOfAny(text, "+-", pos, text.length()), pos) + 1; + exponentMark = text.substring(pos, pos2); + exponent = text.substring(pos2); + text = text.substring(0, pos); + } + + pos = text.indexOf('.'); + if (pos >= 0) { + hasPoint = true; + fractional = text.substring(pos + 1); + text = text.substring(0, pos); + } + } + + final StringBuilder buffer = StringBuilderSpinAllocator.alloc(); + try { + buffer.append(prefix); + appendFromEnd(buffer, text, groupSize); + if (isFP) { + if (hasPoint) buffer.append('.'); + appendFromStart(buffer, fractional, groupSize); + buffer.append(exponentMark); + appendFromEnd(buffer, exponent, 3); // exponent is always decimal + } + buffer.append(suffix); + return buffer.toString(); + } + finally { + StringBuilderSpinAllocator.dispose(buffer); + } + } + + private static void appendFromEnd(final StringBuilder buffer, final String original, final int groupSize) { + final int position = buffer.length(); + int pointer = original.length(); + while (pointer > groupSize) { + buffer.insert(position, original.substring(pointer - groupSize, pointer)); + buffer.insert(position, '_'); + pointer -= groupSize; + } + + if (pointer > 0) { + buffer.insert(position, original.substring(0, pointer)); + } + } + + private static void appendFromStart(final StringBuilder buffer, final String original, final int groupSize) { + int pointer = 0; + while (pointer + groupSize < original.length()) { + buffer.append(original.substring(pointer, pointer + groupSize)); + buffer.append('_'); + pointer += groupSize; + } + + if (pointer < original.length()) { + buffer.append(original.substring(pointer)); + } } } diff --git a/java/java-tests/testData/codeInsight/underscoresInLiterals/WithUnderscores.java b/java/java-tests/testData/codeInsight/underscoresInLiterals/WithUnderscores.java new file mode 100644 index 000000000000..1a51452accb2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/underscoresInLiterals/WithUnderscores.java @@ -0,0 +1,3 @@ +class C { + int i = 123_456_789; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/underscoresInLiterals/WithoutUnderscores.java b/java/java-tests/testData/codeInsight/underscoresInLiterals/WithoutUnderscores.java new file mode 100644 index 000000000000..9ea0dac0a8cd --- /dev/null +++ b/java/java-tests/testData/codeInsight/underscoresInLiterals/WithoutUnderscores.java @@ -0,0 +1,3 @@ +class C { + int i = 123456789; +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/intention/UnderscoresInLiteralsFormatterTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/intention/UnderscoresInLiteralsFormatterTest.java new file mode 100644 index 000000000000..c49d4f2981ca --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/intention/UnderscoresInLiteralsFormatterTest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2000-2011 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 com.intellij.codeInsight.intention; + +import com.intellij.psi.PsiType; +import com.intellij.util.text.LiteralFormatUtil; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class UnderscoresInLiteralsFormatterTest { + @Test + public void testIntegers() { + assertEquals("0", LiteralFormatUtil.format("0", PsiType.INT)); + assertEquals("12", LiteralFormatUtil.format("12", PsiType.INT)); + assertEquals("123", LiteralFormatUtil.format("123", PsiType.INT)); + assertEquals("123L", LiteralFormatUtil.format("123L", PsiType.INT)); + assertEquals("1_234", LiteralFormatUtil.format("1234", PsiType.INT)); + assertEquals("123_456", LiteralFormatUtil.format("123456", PsiType.INT)); + assertEquals("1_234_567_890l", LiteralFormatUtil.format("1234567890l", PsiType.LONG)); + + assertEquals("0x", LiteralFormatUtil.format("0x", PsiType.INT)); + assertEquals("0xL", LiteralFormatUtil.format("0xL", PsiType.LONG)); + assertEquals("0x1L", LiteralFormatUtil.format("0x1L", PsiType.LONG)); + assertEquals("0x1234", LiteralFormatUtil.format("0x1234", PsiType.INT)); + assertEquals("0x1234l", LiteralFormatUtil.format("0x1234l", PsiType.LONG)); + assertEquals("0x1_abcd", LiteralFormatUtil.format("0x1abcd", PsiType.INT)); + + assertEquals("07", LiteralFormatUtil.format("07", PsiType.INT)); + assertEquals("0_777", LiteralFormatUtil.format("0777", PsiType.INT)); + assertEquals("077_777", LiteralFormatUtil.format("077777", PsiType.INT)); + + assertEquals("0b", LiteralFormatUtil.format("0b", PsiType.INT)); + assertEquals("0b1010", LiteralFormatUtil.format("0b1010", PsiType.INT)); + assertEquals("0b0_1010", LiteralFormatUtil.format("0b01010", PsiType.INT)); + assertEquals("0b1010_1010", LiteralFormatUtil.format("0b10101010", PsiType.INT)); + assertEquals("0b1010_1010L", LiteralFormatUtil.format("0b10101010L", PsiType.LONG)); + } + + @Test + public void testDecimalFloatingPoints() { + assertEquals("1f", LiteralFormatUtil.format("1f", PsiType.FLOAT)); + assertEquals("123f", LiteralFormatUtil.format("123f", PsiType.FLOAT)); + assertEquals("1_234f", LiteralFormatUtil.format("1234f", PsiType.FLOAT)); + assertEquals("1_234d", LiteralFormatUtil.format("1234d", PsiType.DOUBLE)); + + assertEquals("1.", LiteralFormatUtil.format("1.", PsiType.DOUBLE)); + assertEquals("1.f", LiteralFormatUtil.format("1.f", PsiType.FLOAT)); + assertEquals("123.", LiteralFormatUtil.format("123.", PsiType.DOUBLE)); + assertEquals("123.f", LiteralFormatUtil.format("123.f", PsiType.FLOAT)); + assertEquals("1_234.", LiteralFormatUtil.format("1234.", PsiType.DOUBLE)); + assertEquals("1_234.d", LiteralFormatUtil.format("1234.d", PsiType.FLOAT)); + assertEquals("1_234.f", LiteralFormatUtil.format("1234.f", PsiType.FLOAT)); + + assertEquals(".1", LiteralFormatUtil.format(".1", PsiType.DOUBLE)); + assertEquals(".1f", LiteralFormatUtil.format(".1f", PsiType.FLOAT)); + assertEquals(".123", LiteralFormatUtil.format(".123", PsiType.DOUBLE)); + assertEquals(".123f", LiteralFormatUtil.format(".123f", PsiType.FLOAT)); + assertEquals(".123_4", LiteralFormatUtil.format(".1234", PsiType.DOUBLE)); + assertEquals(".123_4f", LiteralFormatUtil.format(".1234f", PsiType.FLOAT)); + assertEquals(".123_456", LiteralFormatUtil.format(".123456", PsiType.DOUBLE)); + assertEquals(".123_456d", LiteralFormatUtil.format(".123456d", PsiType.DOUBLE)); + assertEquals(".123_456f", LiteralFormatUtil.format(".123456f", PsiType.FLOAT)); + + assertEquals("1.1", LiteralFormatUtil.format("1.1", PsiType.DOUBLE)); + assertEquals("1.1f", LiteralFormatUtil.format("1.1f", PsiType.FLOAT)); + assertEquals("123.123", LiteralFormatUtil.format("123.123", PsiType.DOUBLE)); + assertEquals("123.123f", LiteralFormatUtil.format("123.123f", PsiType.FLOAT)); + assertEquals("1_234.123_4", LiteralFormatUtil.format("1234.1234", PsiType.DOUBLE)); + assertEquals("1_234.123_4f", LiteralFormatUtil.format("1234.1234f", PsiType.FLOAT)); + + assertEquals("1.1e0", LiteralFormatUtil.format("1.1e0", PsiType.DOUBLE)); + assertEquals("1.1E0f", LiteralFormatUtil.format("1.1E0f", PsiType.FLOAT)); + assertEquals("123.123e+123", LiteralFormatUtil.format("123.123e+123", PsiType.DOUBLE)); + assertEquals("123.123e-123f", LiteralFormatUtil.format("123.123e-123f", PsiType.FLOAT)); + assertEquals("1_234.123_4e1_000", LiteralFormatUtil.format("1234.1234e1000", PsiType.DOUBLE)); + assertEquals("1_234.123_4e1_000f", LiteralFormatUtil.format("1234.1234e1000f", PsiType.FLOAT)); + } + + @Test + public void testHexFloatingPoints() { + assertEquals("0xp1", LiteralFormatUtil.format("0xp1", PsiType.DOUBLE)); + assertEquals("0xp1f", LiteralFormatUtil.format("0xp1f", PsiType.FLOAT)); + + assertEquals("0x1p1", LiteralFormatUtil.format("0x1p1", PsiType.DOUBLE)); + assertEquals("0x1p1f", LiteralFormatUtil.format("0x1p1f", PsiType.FLOAT)); + assertEquals("0x1234p+1", LiteralFormatUtil.format("0x1234p+1", PsiType.DOUBLE)); + assertEquals("0x1234p-1f", LiteralFormatUtil.format("0x1234p-1f", PsiType.FLOAT)); + assertEquals("0x1_2345p1", LiteralFormatUtil.format("0x12345p1", PsiType.DOUBLE)); + assertEquals("0x1_2345p1f", LiteralFormatUtil.format("0x12345p1f", PsiType.FLOAT)); + + assertEquals("0x1.p1", LiteralFormatUtil.format("0x1.p1", PsiType.DOUBLE)); + assertEquals("0x1.p1f", LiteralFormatUtil.format("0x1.p1f", PsiType.FLOAT)); + assertEquals("0x1234.p+1", LiteralFormatUtil.format("0x1234.p+1", PsiType.DOUBLE)); + assertEquals("0x1234.p-1f", LiteralFormatUtil.format("0x1234.p-1f", PsiType.FLOAT)); + assertEquals("0x1_2345.p1", LiteralFormatUtil.format("0x12345.p1", PsiType.DOUBLE)); + assertEquals("0x1_2345.p1f", LiteralFormatUtil.format("0x12345.p1f", PsiType.FLOAT)); + + assertEquals("0x.1p1", LiteralFormatUtil.format("0x.1p1", PsiType.DOUBLE)); + assertEquals("0x.1p1f", LiteralFormatUtil.format("0x.1p1f", PsiType.FLOAT)); + assertEquals("0x.1234p+1", LiteralFormatUtil.format("0x.1234p+1", PsiType.DOUBLE)); + assertEquals("0x.1234p-1f", LiteralFormatUtil.format("0x.1234p-1f", PsiType.FLOAT)); + assertEquals("0x.1234_5p1", LiteralFormatUtil.format("0x.12345p1", PsiType.DOUBLE)); + assertEquals("0x.1234_5p1f", LiteralFormatUtil.format("0x.12345p1f", PsiType.FLOAT)); + + assertEquals("0x1.1p+1", LiteralFormatUtil.format("0x1.1p+1", PsiType.DOUBLE)); + assertEquals("0x1.1p-1f", LiteralFormatUtil.format("0x1.1p-1f", PsiType.FLOAT)); + assertEquals("0xabcd.1234p+100", LiteralFormatUtil.format("0xabcd.1234p+100", PsiType.DOUBLE)); + assertEquals("0xabcd.1234P-100f", LiteralFormatUtil.format("0xabcd.1234P-100f", PsiType.FLOAT)); + assertEquals("0xab_cdef.1234_5p+1_024", LiteralFormatUtil.format("0xabcdef.12345p+1024", PsiType.DOUBLE)); + assertEquals("0xab_cdef.1234_5P-1_024f", LiteralFormatUtil.format("0xabcdef.12345P-1024f", PsiType.FLOAT)); + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/intention/UnderscoresInLiteralsTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/intention/UnderscoresInLiteralsTest.java new file mode 100644 index 000000000000..2e9babf60e2f --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/intention/UnderscoresInLiteralsTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2011 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 com.intellij.codeInsight.intention; + +import com.intellij.JavaTestUtil; +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.builders.JavaModuleFixtureBuilder; +import com.intellij.testFramework.fixtures.CodeInsightTestUtil; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; + +public class UnderscoresInLiteralsTest extends JavaCodeInsightFixtureTestCase { + @Override + protected void tuneFixture(final JavaModuleFixtureBuilder moduleBuilder) throws Exception { + super.tuneFixture(moduleBuilder); + moduleBuilder.setLanguageLevel(LanguageLevel.JDK_1_7); + } + + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/underscoresInLiterals/"; + } + + public void testRemove() throws Exception { + final String removeIntention = CodeInsightBundle.message("intention.remove.literal.underscores"); + CodeInsightTestUtil.doIntentionTest(myFixture, removeIntention, "WithUnderscores.java", "WithoutUnderscores.java"); + } + + public void testInsert() throws Exception { + final String insertIntention = CodeInsightBundle.message("intention.insert.literal.underscores"); + CodeInsightTestUtil.doIntentionTest(myFixture, insertIntention, "WithoutUnderscores.java", "WithUnderscores.java"); + } +} diff --git a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties index 482e99c8a68e..d5bb8acb132e 100644 --- a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties +++ b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties @@ -186,6 +186,9 @@ intention.implement.abstract.method.error.no.classes.title=No Classes Found intention.implement.abstract.method.class.chooser.title=Choose Implementing Class intention.implement.abstract.method.command.name=Implement method intention.invert.if.condition=Invert If Condition +intention.underscores.in.literals.family=Underscores in numeric literals +intention.remove.literal.underscores=Remove underscores from literal +intention.insert.literal.underscores=Insert underscores into literal intention.create.test=Create Test intention.create.test.dialog.testing.library=Testing library: diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index 91b56ed57dec..50525a8295f3 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -1217,13 +1217,18 @@ public class StringUtil { public static boolean containsAnyChar(@NotNull final String value, @NotNull final String chars) { for (int i = 0; i < chars.length(); i++) { - if (value.indexOf(chars.charAt(i)) != -1) return true; + if (containsChar(value, chars.charAt(i))) return true; } return false; } - public static String firstLetterToUpperCase(String displayString) { + public static boolean containsChar(final String value, final char ch) { + return value.indexOf(ch) >= 0; + } + + @Nullable + public static String firstLetterToUpperCase(@Nullable final String displayString) { if (displayString == null || displayString.length() == 0) return displayString; char firstChar = displayString.charAt(0); char uppedFirstChar = toUpperCase(firstChar); @@ -1243,8 +1248,8 @@ public class StringUtil { * @return stripped string e.g. "mystring" */ @NotNull - public static String strip(@NotNull final String s, @NotNull CharFilter filter) { - StringBuilder result = new StringBuilder(s.length()); + public static String strip(@NotNull final String s, @NotNull final CharFilter filter) { + final StringBuilder result = new StringBuilder(s.length()); for (int i = 0; i < s.length(); i++) { char ch = s.charAt(i); if (filter.accept(ch)) { @@ -1376,6 +1381,17 @@ public class StringUtil { return i + subString.length(); } + public static int indexOfAny(final String s, final String chars) { + return indexOfAny(s, chars, 0, s.length()); + } + + public static int indexOfAny(final String s, final String chars, final int start, final int end) { + for (int i = start; i < end; i++) { + if (containsChar(chars, s.charAt(i))) return i; + } + return -1; + } + public static String substringAfter(@NotNull String text, @NotNull String subString) { int i = text.indexOf(subString); if (i == -1) return null; @@ -1842,16 +1858,4 @@ public class StringUtil { } return true; } - - public static String cleanString(@NotNull final String original, final String charsToRemove) { - if (original.length() == 0) return original; - - final StringBuilder builder = new StringBuilder(original.length()); - for (int i = 0; i < original.length(); i++) { - final char ch = original.charAt(i); - if (charsToRemove.indexOf(ch) < 0) builder.append(ch); - } - - return builder.toString(); - } } diff --git a/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/after.java.template b/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/after.java.template new file mode 100644 index 000000000000..04d07a6d11de --- /dev/null +++ b/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/after.java.template @@ -0,0 +1 @@ +float pi = 3.141_592_654f; \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/before.java.template b/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/before.java.template new file mode 100644 index 000000000000..4f5787170d8c --- /dev/null +++ b/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/before.java.template @@ -0,0 +1 @@ +float pi = 3.141592654f; \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/description.html b/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/description.html new file mode 100644 index 000000000000..aba45639d8a2 --- /dev/null +++ b/resources-en/src/intentionDescriptions/InsertLiteralUnderscoresAction/description.html @@ -0,0 +1,5 @@ + + +This intention inserts underscores into numeric literals (supported in JDK7 amd higher). + + diff --git a/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/after.java.template b/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/after.java.template new file mode 100644 index 000000000000..c7d89604c97f --- /dev/null +++ b/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/after.java.template @@ -0,0 +1 @@ +float pi = 3.141592654f; \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/before.java.template b/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/before.java.template new file mode 100644 index 000000000000..edeeec45cf49 --- /dev/null +++ b/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/before.java.template @@ -0,0 +1 @@ +float pi = 3.141_592_654f; \ No newline at end of file diff --git a/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/description.html b/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/description.html new file mode 100644 index 000000000000..b6cf8c447aed --- /dev/null +++ b/resources-en/src/intentionDescriptions/RemoveLiteralUnderscoresAction/description.html @@ -0,0 +1,5 @@ + + +This intention removes underscores from numeric literals. + + diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 57b6ac2cab12..ee8f6311b4bc 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -620,6 +620,14 @@ com.intellij.codeInspection.concurrencyAnnotations.JCiPOrderEntryFix + + com.intellij.codeInsight.intention.impl.RemoveLiteralUnderscoresAction + Numbers + + + com.intellij.codeInsight.intention.impl.InsertLiteralUnderscoresAction + Numbers +