string -> raw: process leading/tailing tics (IDEA-189855)

This commit is contained in:
Anna.Kozlova
2018-04-13 20:06:16 +02:00
parent 3de00fcc41
commit f77bc9ff0d
7 changed files with 101 additions and 39 deletions
@@ -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)));
}
}
}
@@ -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);
@@ -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 <code>text</code> contains <code>tics</code>, 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 <code>text</code>
*/
public static int getLeadingTicsSequence(String text) {
return getTicsSequence(text, text.length(), 0);
}
/**
* Return number of tailing tics in the <code>text</code>
*/
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;
}
}
@@ -0,0 +1,5 @@
class A {
{
String s = "````" + `a` + "`";
}
}
@@ -0,0 +1,5 @@
class A {
{
String s = "````<caret>a`";
}
}
@@ -0,0 +1,5 @@
class A {
{
String s = "````<caret>`";
}
}
@@ -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"));
}