mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
preserve text blocks when converting to (message)format string (IDEA-217253, IDEA-218831)
and use new text block escaping GitOrigin-RevId: cc8eea8aa0c638b967e40b6a6b4b9dea1cfed61d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f5bd40eb0c
commit
a12eee9fbc
+12
-18
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.
|
||||
*/
|
||||
// Copyright 2000-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 file.
|
||||
package com.intellij.codeInsight.intention.impl;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
@@ -20,10 +6,12 @@ import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
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.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
|
||||
import com.intellij.psi.util.PsiConcatenationUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -33,6 +21,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -56,15 +45,20 @@ public class ConcatenationToMessageFormatAction implements IntentionAction {
|
||||
final PsiElement element = findElementAtCaret(editor, file);
|
||||
PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(element);
|
||||
if (concatenation == null) return;
|
||||
StringBuilder formatString = new StringBuilder();
|
||||
List<PsiExpression> args = new ArrayList<>();
|
||||
PsiConcatenationUtil.buildFormatString(concatenation, formatString, args, false);
|
||||
final String formatString = PsiConcatenationUtil.buildFormatString(concatenation, false, args);
|
||||
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
|
||||
PsiMethodCallExpression call = (PsiMethodCallExpression)
|
||||
factory.createExpressionFromText("java.text.MessageFormat.format()", concatenation);
|
||||
PsiExpressionList argumentList = call.getArgumentList();
|
||||
PsiExpression formatArgument = factory.createExpressionFromText("\"" + formatString.toString() + "\"", null);
|
||||
boolean textBlocks = Arrays.stream(concatenation.getOperands())
|
||||
.anyMatch(operand -> operand instanceof PsiLiteralExpressionImpl &&
|
||||
((PsiLiteralExpressionImpl)operand).getLiteralElementType() == JavaTokenType.TEXT_BLOCK_LITERAL);
|
||||
final String expressionText = textBlocks
|
||||
? "\"\"\"\n" + StringUtil.escapeTextBlockCharacters(formatString) + "\"\"\""
|
||||
: "\"" + StringUtil.escapeStringCharacters(formatString) + "\"";
|
||||
PsiExpression formatArgument = factory.createExpressionFromText(expressionText, null);
|
||||
argumentList.add(formatArgument);
|
||||
if (PsiUtil.isLanguageLevel5OrHigher(file)) {
|
||||
for (PsiExpression arg : args) {
|
||||
|
||||
@@ -1,21 +1,6 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.
|
||||
*/
|
||||
// Copyright 2000-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 file.
|
||||
package com.intellij.psi.util;
|
||||
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -28,17 +13,29 @@ import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
|
||||
|
||||
public class PsiConcatenationUtil {
|
||||
|
||||
/**
|
||||
* @param formatParameters output parameter, will contain the format parameters found in the concatenation
|
||||
* @return unescaped (!) format string produced from the concatenation
|
||||
*/
|
||||
public static String buildFormatString(PsiExpression concatenation, boolean printfFormat,
|
||||
List<? super PsiExpression> formatParameters) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
buildFormatString(concatenation, result, formatParameters, printfFormat);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
// externally used
|
||||
public static void buildFormatString(PsiExpression expression, StringBuilder formatString,
|
||||
List<? super PsiExpression> formatParameters, boolean printfFormat) {
|
||||
if (expression instanceof PsiLiteralExpression) {
|
||||
final PsiLiteralExpression literalExpression = (PsiLiteralExpression) expression;
|
||||
TextRange range = ElementManipulators.getValueTextRange(expression);
|
||||
String formatText = range.substring(literalExpression.getText());
|
||||
final String text = String.valueOf(literalExpression.getValue());
|
||||
String formatText;
|
||||
if (printfFormat) {
|
||||
formatText = formatText.replace("%", "%%").replace("\\'", "'");
|
||||
formatText = text.replace("%", "%%").replace("\\'", "'");
|
||||
}
|
||||
else {
|
||||
formatText = formatText.replace("'", "''").replaceAll("((\\{|})+)", "'$1'");
|
||||
formatText = text.replace("'", "''").replaceAll("([{}]+)", "'$1'");
|
||||
}
|
||||
formatString.append(formatText);
|
||||
} else if (expression instanceof PsiPolyadicExpression) {
|
||||
@@ -46,18 +43,21 @@ public class PsiConcatenationUtil {
|
||||
if (type != null && type.equalsToText(JAVA_LANG_STRING)) {
|
||||
final PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression) expression;
|
||||
PsiExpression[] operands = binaryExpression.getOperands();
|
||||
PsiType left = operands[0].getType();
|
||||
boolean stringStarted = left != null && left.equalsToText(JAVA_LANG_STRING);
|
||||
PsiType first = operands[0].getType();
|
||||
PsiType second = operands[1].getType();
|
||||
boolean stringStarted = first != null && first.equalsToText(JAVA_LANG_STRING) ||
|
||||
second != null && second.equalsToText(JAVA_LANG_STRING);
|
||||
if (stringStarted) {
|
||||
buildFormatString(operands[0], formatString, formatParameters, printfFormat);
|
||||
}
|
||||
for (int i = 1; i < operands.length; i++) {
|
||||
PsiExpression op = operands[i];
|
||||
PsiType optype = op.getType();
|
||||
PsiType r = TypeConversionUtil.calcTypeForBinaryExpression(left, optype, binaryExpression.getOperationTokenType(), true);
|
||||
PsiType r = TypeConversionUtil.calcTypeForBinaryExpression(first, optype, binaryExpression.getOperationTokenType(), true);
|
||||
if (r != null && r.equalsToText(JAVA_LANG_STRING) && !stringStarted) {
|
||||
stringStarted = true;
|
||||
PsiElement element = binaryExpression.getTokenBeforeOperand(op);
|
||||
assert element != null;
|
||||
if (element.getPrevSibling() instanceof PsiWhiteSpace) element = element.getPrevSibling();
|
||||
String text = binaryExpression.getText().substring(0, element.getStartOffsetInParent());
|
||||
PsiExpression subExpression = JavaPsiFacade.getElementFactory(binaryExpression.getProject())
|
||||
@@ -72,7 +72,7 @@ public class PsiConcatenationUtil {
|
||||
addFormatParameter(op, formatString, formatParameters, printfFormat);
|
||||
}
|
||||
}
|
||||
left = r;
|
||||
first = r;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
class C {
|
||||
private static String quote(String s) {
|
||||
return java.text.MessageFormat.format("\"{0}\"", s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class C {
|
||||
private static String quote(String s) {
|
||||
return '"' + <caret>s + '"';
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
class C {
|
||||
|
||||
void x(int a, int b) {
|
||||
//keep me
|
||||
String s = java.text.MessageFormat.format("""
|
||||
the text
|
||||
block
|
||||
line2
|
||||
{0}{1} "to" be""", a, b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
class C {
|
||||
|
||||
void x(int a, int b) {
|
||||
String s = """
|
||||
the text\n block
|
||||
line2
|
||||
""" +
|
||||
a + b + <caret>//keep me
|
||||
" \"to\" be";
|
||||
}
|
||||
}
|
||||
+7
-21
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2017 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.
|
||||
*/
|
||||
// Copyright 2000-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 file.
|
||||
package com.intellij.java.codeInsight.intention;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
@@ -39,6 +25,8 @@ public class ConcatenationToMessageFormatTest extends JavaCodeInsightFixtureTest
|
||||
}
|
||||
|
||||
public void testComments() { doTest(); }
|
||||
public void testTextblock() { doTest(); }
|
||||
public void testEscaping() { doTest(); }
|
||||
|
||||
private void doTest() {
|
||||
final String name = getTestName(true);
|
||||
@@ -53,14 +41,12 @@ public class ConcatenationToMessageFormatTest extends JavaCodeInsightFixtureTest
|
||||
|
||||
private void doTest(String expressionText, String messageFormatText, String... foundExpressionTexts) {
|
||||
final PsiExpression expression = getElementFactory().createExpressionFromText(expressionText, null);
|
||||
final StringBuilder result = new StringBuilder();
|
||||
final ArrayList<PsiExpression> args = new ArrayList<>();
|
||||
PsiConcatenationUtil.buildFormatString(expression, result, args, false);
|
||||
assertEquals(messageFormatText, result.toString());
|
||||
final String formatString = PsiConcatenationUtil.buildFormatString(expression, false, args);
|
||||
assertEquals(messageFormatText, formatString);
|
||||
assertEquals(foundExpressionTexts.length, args.size());
|
||||
for (int i = 0; i < foundExpressionTexts.length; i++) {
|
||||
final String foundExpressionText = foundExpressionTexts[i];
|
||||
assertEquals(foundExpressionText, args.get(i).getText());
|
||||
assertEquals(foundExpressionTexts[i], args.get(i).getText());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +55,7 @@ public class ConcatenationToMessageFormatTest extends JavaCodeInsightFixtureTest
|
||||
}
|
||||
|
||||
public void test2() {
|
||||
doTest("1 + 2 + 3 + \"{}'\" + '\\n' + ((java.lang.String)ccc)", "{0}'{}'''\\n{1}", "1 + 2 + 3", "ccc");
|
||||
doTest("1 + 2 + 3 + \"{}'\" + '\\n' + ((java.lang.String)ccc)", "{0}'{}'''\n{1}", "1 + 2 + 3", "ccc");
|
||||
}
|
||||
|
||||
public void test3() {
|
||||
|
||||
+21
-26
@@ -1,20 +1,7 @@
|
||||
/*
|
||||
* Copyright 2008-2018 Bas Leijdekkers
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
// Copyright 2000-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 file.
|
||||
package com.siyeh.ipp.concatenation;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl;
|
||||
import com.intellij.psi.util.PsiConcatenationUtil;
|
||||
@@ -45,9 +32,8 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention {
|
||||
expression = (PsiPolyadicExpression)parent;
|
||||
parent = expression.getParent();
|
||||
}
|
||||
final StringBuilder formatString = new StringBuilder();
|
||||
final List<PsiExpression> formatParameters = new ArrayList<>();
|
||||
PsiConcatenationUtil.buildFormatString(expression, formatString, formatParameters, true);
|
||||
final String formatString = PsiConcatenationUtil.buildFormatString(expression, true, formatParameters);
|
||||
if (replaceWithPrintfExpression(expression, formatString, formatParameters)) {
|
||||
return;
|
||||
}
|
||||
@@ -96,7 +82,7 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention {
|
||||
}
|
||||
final String qualifiedName = containingClass.getQualifiedName();
|
||||
if (!"java.io.PrintStream".equals(qualifiedName) &&
|
||||
!"java.io.Printwriter".equals(qualifiedName)) {
|
||||
!"java.io.PrintWriter".equals(qualifiedName)) {
|
||||
return false;
|
||||
}
|
||||
CommentTracker commentTracker = new CommentTracker();
|
||||
@@ -106,9 +92,9 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention {
|
||||
newExpression.append(commentTracker.text(qualifier)).append('.');
|
||||
}
|
||||
newExpression.append("printf(");
|
||||
appendFormatString(expression, formatString, insertNewline, newExpression);
|
||||
appendFormatString(expression, formatString.toString(), insertNewline, newExpression);
|
||||
for (PsiExpression formatParameter : formatParameters) {
|
||||
newExpression.append(", ").append(commentTracker.text(formatParameter));
|
||||
newExpression.append(",").append(commentTracker.text(formatParameter));
|
||||
}
|
||||
newExpression.append(')');
|
||||
PsiReplacementUtil.replaceExpression(methodCallExpression, newExpression.toString(), commentTracker);
|
||||
@@ -116,17 +102,26 @@ public class ReplaceConcatenationWithFormatStringIntention extends Intention {
|
||||
}
|
||||
|
||||
private static void appendFormatString(PsiPolyadicExpression expression,
|
||||
CharSequence formatString,
|
||||
String formatString,
|
||||
boolean insertNewline,
|
||||
StringBuilder newExpression) {
|
||||
boolean textBlocks = Arrays.stream(expression.getOperands())
|
||||
.anyMatch(operand -> operand instanceof PsiLiteralExpressionImpl &&
|
||||
((PsiLiteralExpressionImpl)operand).getLiteralElementType() == JavaTokenType.TEXT_BLOCK_LITERAL);
|
||||
newExpression.append(textBlocks ? "\"\"\"\n" : '\"');
|
||||
newExpression.append(formatString);
|
||||
if (insertNewline) {
|
||||
newExpression.append("%n");
|
||||
if (textBlocks) {
|
||||
newExpression.append("\"\"\"\n");
|
||||
newExpression.append(StringUtil.escapeTextBlockCharacters(formatString));
|
||||
if (insertNewline) {
|
||||
newExpression.append('\n');
|
||||
}
|
||||
newExpression.append("\"\"\"");
|
||||
} else {
|
||||
newExpression.append('\"');
|
||||
newExpression.append(StringUtil.escapeStringCharacters(formatString));
|
||||
if (insertNewline) {
|
||||
newExpression.append("%n");
|
||||
}
|
||||
newExpression.append('\"');
|
||||
}
|
||||
newExpression.append(textBlocks ? "\"\"\"" : '\"');
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,7 +1,8 @@
|
||||
class C {
|
||||
//keep me
|
||||
String s = String.format("""
|
||||
the text\n block
|
||||
lin<caret>e2
|
||||
%d%d to be""", 1, 2);
|
||||
the text
|
||||
block
|
||||
line2
|
||||
%d%d t<caret>o be""", 1, 2);
|
||||
}
|
||||
+7
-18
@@ -1,18 +1,4 @@
|
||||
/*
|
||||
* Copyright 2000-2015 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.
|
||||
*/
|
||||
// Copyright 2000-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 file.
|
||||
package com.intellij.codeInspection.i18n;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
@@ -21,6 +7,7 @@ import com.intellij.lang.properties.psi.PropertiesFile;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiConcatenationUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
@@ -49,6 +36,7 @@ public class I18nizeConcatenationQuickFix extends I18nizeQuickFix{
|
||||
@Override
|
||||
public JavaI18nizeQuickFixDialog createDialog(Project project, Editor editor, PsiFile psiFile) {
|
||||
PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(psiFile, editor);
|
||||
assert concatenation != null;
|
||||
PsiLiteralExpression literalExpression = getContainingLiteral(concatenation);
|
||||
if (literalExpression == null) return null;
|
||||
return createDialog(project, psiFile, literalExpression);
|
||||
@@ -66,6 +54,7 @@ public class I18nizeConcatenationQuickFix extends I18nizeQuickFix{
|
||||
@Nullable PsiLiteralExpression literalExpression,
|
||||
String i18nizedText) throws IncorrectOperationException {
|
||||
PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(psiFile, editor);
|
||||
assert concatenation != null;
|
||||
PsiExpression expression = JavaPsiFacade.getInstance(psiFile.getProject()).getElementFactory().createExpressionFromText(i18nizedText, concatenation);
|
||||
return concatenation.replace(expression);
|
||||
}
|
||||
@@ -77,16 +66,16 @@ public class I18nizeConcatenationQuickFix extends I18nizeQuickFix{
|
||||
@Override
|
||||
protected JavaI18nizeQuickFixDialog createDialog(final Project project, final PsiFile context, final PsiLiteralExpression literalExpression) {
|
||||
PsiPolyadicExpression concatenation = getEnclosingLiteralConcatenation(literalExpression);
|
||||
StringBuilder formatString = new StringBuilder();
|
||||
String formatString = "";
|
||||
final List<PsiExpression> args = new ArrayList<>();
|
||||
try {
|
||||
PsiConcatenationUtil.buildFormatString(concatenation, formatString, args, false);
|
||||
formatString = StringUtil.escapeStringCharacters(PsiConcatenationUtil.buildFormatString(concatenation, false, args));
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
return new JavaI18nizeQuickFixDialog(project, context, literalExpression, formatString.toString(), null, true, true) {
|
||||
return new JavaI18nizeQuickFixDialog(project, context, literalExpression, formatString, null, true, true) {
|
||||
@Override
|
||||
@Nullable
|
||||
protected String getTemplateName() {
|
||||
|
||||
Reference in New Issue
Block a user