From 3de00fcc417592605265686b7d7c04f3f2d36e0f Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Fri, 13 Apr 2018 18:31:18 +0200 Subject: [PATCH] convert string to raw string literal intention (IDEA-189855) --- .../src/messages/QuickFixBundle.properties | 1 + java/java-impl/src/META-INF/JavaPlugin.xml | 4 ++ .../ConvertStringLiteralToRawAction.java | 70 +++++++++++++++++++ .../RawStringLiteralPasteProcessor.java | 8 ++- .../after.java.template | 5 ++ .../before.java.template | 3 + .../description.html | 5 ++ .../StringToRawTransformation.after.java | 7 ++ .../StringToRawTransformation.java | 7 ++ ...RawTransformationWithTicsInside.after.java | 7 ++ ...ringToRawTransformationWithTicsInside.java | 7 ++ .../daemon/LightAdvRawStringLiteralsTest.java | 8 +++ ...oncatenatedStringToClipboardIntention.java | 9 +-- 13 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertStringLiteralToRawAction.java create mode 100644 java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/after.java.template create mode 100644 java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/before.java.template create mode 100644 java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/description.html create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformation.after.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformation.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationWithTicsInside.after.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationWithTicsInside.java diff --git a/java/java-analysis-impl/src/messages/QuickFixBundle.properties b/java/java-analysis-impl/src/messages/QuickFixBundle.properties index 28cae2301bb2..c5e43b6ae04f 100644 --- a/java/java-analysis-impl/src/messages/QuickFixBundle.properties +++ b/java/java-analysis-impl/src/messages/QuickFixBundle.properties @@ -270,6 +270,7 @@ change.to.append.text=Change to ''{0}'' convert.to.string.family=Fix Character Literal convert.to.string.text=Convert to string literal +convert.to.raw.string.text=Convert to raw string literal initialize.final.field.in.constructor.name=Initialize in constructor initialize.final.field.in.constructor.choose.dialog.title=Choose constructors to add initialization to diff --git a/java/java-impl/src/META-INF/JavaPlugin.xml b/java/java-impl/src/META-INF/JavaPlugin.xml index 12a841f21d7e..2931cde60a6f 100644 --- a/java/java-impl/src/META-INF/JavaPlugin.xml +++ b/java/java-impl/src/META-INF/JavaPlugin.xml @@ -949,6 +949,10 @@ com.intellij.codeInsight.daemon.impl.quickfix.ConvertRawToStringLiteralAction Java/Strings + + com.intellij.codeInsight.daemon.impl.quickfix.ConvertStringLiteralToRawAction + Java/Strings + com.intellij.codeInsight.intention.impl.InsertLiteralUnderscoresAction Java/Numbers diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertStringLiteralToRawAction.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertStringLiteralToRawAction.java new file mode 100644 index 000000000000..fcc6b1cfa6b6 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ConvertStringLiteralToRawAction.java @@ -0,0 +1,70 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.codeInsight.daemon.impl.quickfix; + +import com.intellij.codeInsight.daemon.QuickFixBundle; +import com.intellij.codeInsight.editorActions.RawStringLiteralPasteProcessor; +import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.LowPriorityAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl; +import com.intellij.psi.util.PsiUtil; +import com.intellij.util.IncorrectOperationException; +import com.siyeh.ipp.concatenation.CopyConcatenatedStringToClipboardIntention; +import org.jetbrains.annotations.NotNull; + +public class ConvertStringLiteralToRawAction implements IntentionAction, LowPriorityAction { + @NotNull + @Override + public String getText() { + return QuickFixBundle.message("convert.to.raw.string.text"); + } + + @NotNull + @Override + public String getFamilyName() { + return "Convert to raw string literal"; + } + + @Override + public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { + final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + return PsiUtil.isJavaToken(element, JavaTokenType.STRING_LITERAL) && + PsiUtil.getLanguageLevel(file) == LanguageLevel.JDK_11_PREVIEW; + } + + @Override + public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { + final PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + if (element != null && PsiUtil.isJavaToken(element, JavaTokenType.STRING_LITERAL)) { + PsiElement parent = element.getParent(); + if (parent instanceof PsiLiteralExpressionImpl) { + PsiElement elementToReplace = parent; + String text; + PsiElement gParent = PsiUtil.skipParenthesizedExprUp(parent.getParent()); + if (gParent instanceof PsiPolyadicExpression) { + text = CopyConcatenatedStringToClipboardIntention.buildConcatenationText((PsiPolyadicExpression)gParent); + elementToReplace = gParent; + } + else { + String innerText = ((PsiLiteralExpressionImpl)parent).getInnerText(); + if (innerText == null) return; + text = StringUtil.unescapeStringCharacters(innerText); + } + String additionalQuotes = RawStringLiteralPasteProcessor.getAdditionalQuotes(text, "`"); + PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + CodeStyleManager.getInstance(project).reformat( + elementToReplace.replace(elementFactory.createExpressionFromText('`' + additionalQuotes + text + additionalQuotes + '`', null))); + } + } + } + + @Override + public boolean startInWriteAction() { + return true; + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/RawStringLiteralPasteProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/RawStringLiteralPasteProcessor.java index 9736e120478b..ddec9daaab87 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/RawStringLiteralPasteProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/RawStringLiteralPasteProcessor.java @@ -49,6 +49,12 @@ public class RawStringLiteralPasteProcessor implements PasteProvider { int quotesLength = getQuotesSequence(literalText, length, 0); String quotes = literalText.substring(0, quotesLength); + String additionalQuotes = getAdditionalQuotes(text, quotes); + insertAtCaret(text, additionalQuotes, stringLiteral, editor); + } + + public static String getAdditionalQuotes(String text, String quotes) { + int quotesLength = quotes.length(); int textLength = text.length(); int idx = quotesLength; int maxQuotesNumber = -1; @@ -62,7 +68,7 @@ public class RawStringLiteralPasteProcessor implements PasteProvider { idx += additionalQuotesLength + quotesLength; } - insertAtCaret(text, hasToReplace ? StringUtil.repeat("`", maxQuotesNumber + 1) : "", stringLiteral, editor); + return hasToReplace ? StringUtil.repeat("`", maxQuotesNumber + 1) : ""; } private static int getQuotesSequence(String literalText, int length, int startIndex) { diff --git a/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/after.java.template b/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/after.java.template new file mode 100644 index 000000000000..832fcbde759d --- /dev/null +++ b/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/after.java.template @@ -0,0 +1,5 @@ +String s = `raw +string +literal +`; + diff --git a/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/before.java.template b/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/before.java.template new file mode 100644 index 000000000000..9a935b685c9b --- /dev/null +++ b/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/before.java.template @@ -0,0 +1,3 @@ +String s = "raw\n" + +"string\n" + +"literal"; diff --git a/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/description.html b/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/description.html new file mode 100644 index 000000000000..66d8e57766e3 --- /dev/null +++ b/java/java-impl/src/intentionDescriptions/ConvertStringLiteralToRawAction/description.html @@ -0,0 +1,5 @@ + + +This intention converts (multi-line) string literal into raw string literal. + + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformation.after.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformation.after.java new file mode 100644 index 000000000000..f82346cbf630 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformation.after.java @@ -0,0 +1,7 @@ +class A { + { + String s = `a" +"b +c \`; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformation.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformation.java new file mode 100644 index 000000000000..385c867212d2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformation.java @@ -0,0 +1,7 @@ +class A { + { + String s = "a\"\n" + + "\"b\n" + + "c \\"; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationWithTicsInside.after.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationWithTicsInside.after.java new file mode 100644 index 000000000000..982b3fd52bf5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationWithTicsInside.after.java @@ -0,0 +1,7 @@ +class A { + { + String s = ``a" +"b` +c \``; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationWithTicsInside.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationWithTicsInside.java new file mode 100644 index 000000000000..354c4a1dacce --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationWithTicsInside.java @@ -0,0 +1,7 @@ +class A { + { + String s = "a\"\n" + + "\"b`\n" + + "c \\"; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvRawStringLiteralsTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvRawStringLiteralsTest.java index 126226629c20..7266c41fc0e9 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvRawStringLiteralsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/LightAdvRawStringLiteralsTest.java @@ -36,6 +36,14 @@ public class LightAdvRawStringLiteralsTest extends LightCodeInsightFixtureTestCa doTestIntention(QuickFixBundle.message("convert.to.string.text")); } + public void testStringToRawTransformation() { + doTestIntention(QuickFixBundle.message("convert.to.raw.string.text")); + } + + public void testStringToRawTransformationWithTicsInside() { + doTestIntention(QuickFixBundle.message("convert.to.raw.string.text")); + } + public void testPasteInRawStringLiteral() { doTestPaste("class A {{String s = `q`;}}", "a\nb`\nc", "class A {{String s = ``qa\nb`\nc``;}}"); } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/CopyConcatenatedStringToClipboardIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/CopyConcatenatedStringToClipboardIntention.java index 7780fb4e6bc6..ccc733c4c68a 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/CopyConcatenatedStringToClipboardIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/concatenation/CopyConcatenatedStringToClipboardIntention.java @@ -54,11 +54,12 @@ public class CopyConcatenatedStringToClipboardIntention extends Intention { if (type == null || !type.equalsToText(JAVA_LANG_STRING)) { return; } - final StringBuilder text = buildConcatenationText(concatenationExpression, new StringBuilder()); - CopyPasteManager.getInstance().setContents(new StringSelection(text.toString())); + final String text = buildConcatenationText(concatenationExpression); + CopyPasteManager.getInstance().setContents(new StringSelection(text)); } - private static StringBuilder buildConcatenationText(PsiPolyadicExpression polyadicExpression, StringBuilder out) { + public static String buildConcatenationText(PsiPolyadicExpression polyadicExpression) { + StringBuilder out = new StringBuilder(); for (PsiElement element : polyadicExpression.getChildren()) { if (element instanceof PsiExpression) { final PsiExpression expression = (PsiExpression)element; @@ -75,6 +76,6 @@ public class CopyConcatenatedStringToClipboardIntention extends Intention { out.append('\n'); } } - return out; + return out.toString(); } }