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 index fcc6b1cfa6b6..1f12005af483 100644 --- 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 @@ -2,7 +2,6 @@ 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; @@ -33,8 +32,15 @@ public class ConvertStringLiteralToRawAction implements IntentionAction, LowPrio @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; + if (PsiUtil.isJavaToken(element, JavaTokenType.STRING_LITERAL) && + PsiUtil.getLanguageLevel(file) == LanguageLevel.JDK_11_PREVIEW) { + PsiElement parent = element.getParent(); + if (parent instanceof PsiLiteralExpressionImpl) { + String text = ((PsiLiteralExpressionImpl)parent).getInnerText(); + return text != null && PsiRawStringLiteralUtil.getLeadingTicsSequence(text) < text.length(); + } + } + return false; } @Override @@ -55,10 +61,21 @@ public class ConvertStringLiteralToRawAction implements IntentionAction, LowPrio if (innerText == null) return; text = StringUtil.unescapeStringCharacters(innerText); } - String additionalQuotes = RawStringLiteralPasteProcessor.getAdditionalQuotes(text, "`"); + String prefix = ""; + int startingSeq = PsiRawStringLiteralUtil.getLeadingTicsSequence(text); + if (startingSeq > 0) { + prefix = "\"" + StringUtil.repeat("`", startingSeq) + "\" + "; + } + String suffix = ""; + int tailingSequence = PsiRawStringLiteralUtil.getTailingTicsSequence(text); + if (tailingSequence > 0) { + suffix = "+ \"" + StringUtil.repeat("`", tailingSequence) + "\""; + } + String textTicsTrimmed = text.substring(Math.max(startingSeq, 0), text.length() - Math.max(tailingSequence, 0)); + String additionalQuotes = PsiRawStringLiteralUtil.getAdditionalTics(textTicsTrimmed, "`"); PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); CodeStyleManager.getInstance(project).reformat( - elementToReplace.replace(elementFactory.createExpressionFromText('`' + additionalQuotes + text + additionalQuotes + '`', null))); + elementToReplace.replace(elementFactory.createExpressionFromText(prefix + '`' + additionalQuotes + textTicsTrimmed + additionalQuotes + '`' + suffix, null))); } } } 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 ddec9daaab87..17ee9539477a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/RawStringLiteralPasteProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/RawStringLiteralPasteProcessor.java @@ -13,11 +13,7 @@ import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.ide.CopyPasteManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.JavaTokenType; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -45,38 +41,12 @@ public class RawStringLiteralPasteProcessor implements PasteProvider { if (stringLiteral == null) return; String literalText = stringLiteral.getText(); - int length = literalText.length(); - int quotesLength = getQuotesSequence(literalText, length, 0); - - String quotes = literalText.substring(0, quotesLength); - String additionalQuotes = getAdditionalQuotes(text, quotes); + int ticsLength = PsiRawStringLiteralUtil.getLeadingTicsSequence(literalText); + String quotes = literalText.substring(0, ticsLength); + String additionalQuotes = PsiRawStringLiteralUtil.getAdditionalTics(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; - boolean hasToReplace = false; - while ((idx = text.indexOf(quotes, idx)) > 0 && idx < textLength) { - int additionalQuotesLength = getQuotesSequence(text, textLength, idx + quotesLength); - if (additionalQuotesLength == 0) { - hasToReplace = true; - } - maxQuotesNumber = Math.max(maxQuotesNumber, additionalQuotesLength); - idx += additionalQuotesLength + quotesLength; - } - - return hasToReplace ? StringUtil.repeat("`", maxQuotesNumber + 1) : ""; - } - - private static int getQuotesSequence(String literalText, int length, int startIndex) { - int quotesLength = startIndex; - while (quotesLength < length && literalText.charAt(quotesLength) == '`') quotesLength++; - return quotesLength - startIndex; - } - @Override public boolean isPastePossible(@NotNull DataContext dataContext) { final Editor editor = CommonDataKeys.EDITOR.getData(dataContext); diff --git a/java/java-psi-api/src/com/intellij/psi/PsiRawStringLiteralUtil.java b/java/java-psi-api/src/com/intellij/psi/PsiRawStringLiteralUtil.java new file mode 100644 index 000000000000..71a25bf6bae1 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/PsiRawStringLiteralUtil.java @@ -0,0 +1,51 @@ +// 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.psi; + +import com.intellij.openapi.util.text.StringUtil; + +public class PsiRawStringLiteralUtil { + /** + * Check if text contains tics, and + * if yes, returns the number of additional tics required around text to have a valid raw string literal + * empty string otherwise + */ + public static String getAdditionalTics(String text, String tics) { + int quotesLength = tics.length(); + int textLength = text.length(); + int idx = quotesLength; + int maxQuotesNumber = -1; + boolean hasToReplace = false; + while ((idx = text.indexOf(tics, idx)) > 0 && idx < textLength) { + int additionalQuotesLength = getTicsSequence(text, textLength, idx + quotesLength); + if (additionalQuotesLength == 0) { + hasToReplace = true; + } + maxQuotesNumber = Math.max(maxQuotesNumber, additionalQuotesLength); + idx += additionalQuotesLength + quotesLength; + } + + return hasToReplace ? StringUtil.repeat("`", maxQuotesNumber + 1) : ""; + } + + /** + * Return number of leading tics in the text + */ + public static int getLeadingTicsSequence(String text) { + return getTicsSequence(text, text.length(), 0); + } + + /** + * Return number of tailing tics in the text + */ + public static int getTailingTicsSequence(String text) { + int length = text.length(); + while (length > 0 && text.charAt(length - 1) == '`') length--; + return text.length() - length; + } + + private static int getTicsSequence(String literalText, int length, int startIndex) { + int quotesLength = startIndex; + while (quotesLength < length && literalText.charAt(quotesLength) == '`') quotesLength++; + return quotesLength - startIndex; + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationLeadingTics.after.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationLeadingTics.after.java new file mode 100644 index 000000000000..de0ec8596559 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationLeadingTics.after.java @@ -0,0 +1,5 @@ +class A { + { + String s = "````" + `a` + "`"; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationLeadingTics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationLeadingTics.java new file mode 100644 index 000000000000..9ebf0bf0a509 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationLeadingTics.java @@ -0,0 +1,5 @@ +class A { + { + String s = "````a`"; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationOnlyTics.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationOnlyTics.java new file mode 100644 index 000000000000..26830e023992 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advRawStringLiteral/StringToRawTransformationOnlyTics.java @@ -0,0 +1,5 @@ +class A { + { + String s = "`````"; + } +} \ 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 7266c41fc0e9..cb6b7f53f0c9 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 @@ -40,6 +40,15 @@ public class LightAdvRawStringLiteralsTest extends LightCodeInsightFixtureTestCa doTestIntention(QuickFixBundle.message("convert.to.raw.string.text")); } + public void testStringToRawTransformationLeadingTics() { + doTestIntention(QuickFixBundle.message("convert.to.raw.string.text")); + } + + public void testStringToRawTransformationOnlyTics() { + myFixture.configureByFile(getTestName(false) + ".java"); + assertEmpty(myFixture.filterAvailableIntentions(QuickFixBundle.message("convert.to.raw.string.text"))); + } + public void testStringToRawTransformationWithTicsInside() { doTestIntention(QuickFixBundle.message("convert.to.raw.string.text")); }