mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
java text blocks: split text block; ensure no trailing spaces introduced (IDEA-217012)
GitOrigin-RevId: 500fdf647c90dd8877e39cdbef4e88d0b24806eb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
0ee7900e3a
commit
37116e8226
@@ -1685,6 +1685,10 @@
|
||||
<className>com.intellij.codeInsight.intention.impl.SplitRawStringIntentionAction</className>
|
||||
<category>Java/Strings</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.SplitTextBlockIntentionAction</className>
|
||||
<category>Java/Strings</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>com.intellij.codeInsight.intention.impl.ReplaceCastWithVariableAction</className>
|
||||
<category>Java/Imports</category>
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
// 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.intention.PsiElementBaseIntentionAction;
|
||||
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.impl.source.tree.java.PsiLiteralExpressionImpl;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class SplitTextBlockIntentionAction extends PsiElementBaseIntentionAction {
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
|
||||
if (!(element instanceof PsiJavaToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final PsiJavaToken token = (PsiJavaToken)element;
|
||||
|
||||
if (token.getTokenType() != JavaTokenType.TEXT_BLOCK_LITERAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return token.getText() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
|
||||
if (!(element instanceof PsiJavaToken)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PsiJavaToken token = (PsiJavaToken)element;
|
||||
|
||||
if (token.getTokenType() != JavaTokenType.TEXT_BLOCK_LITERAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final String text = token.getText();
|
||||
if (text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
int splitIdx = offset - token.getTextOffset();
|
||||
|
||||
String firstBlock = StringUtil.trimTrailing(text.substring(0, splitIdx));
|
||||
String lastBlock = StringUtil.trimLeading(text.substring(splitIdx));
|
||||
int indent = ((PsiLiteralExpressionImpl)element.getParent()).getTextBlockIndent();
|
||||
int trimmedSpaces = text.length() - firstBlock.length() - lastBlock.length();
|
||||
int newLineIdx = text.lastIndexOf('\n', splitIdx);
|
||||
if (StringUtil.isEmptyOrSpaces(text.substring(newLineIdx, splitIdx))) {
|
||||
trimmedSpaces -= indent + 1;
|
||||
}
|
||||
String trailingSpaces = trimmedSpaces > 0 ? " + \"" + StringUtil.repeat(" ", trimmedSpaces) + "\""
|
||||
: "";
|
||||
PsiPolyadicExpression replacement = (PsiPolyadicExpression)JavaPsiFacade.getElementFactory(project)
|
||||
.createExpressionFromText(firstBlock + "\"\"\"" +
|
||||
trailingSpaces +
|
||||
" + \"\"\"" + '\n' +
|
||||
StringUtil.repeat(" ", indent) + lastBlock, element);
|
||||
PsiPolyadicExpression replacedExpression = (PsiPolyadicExpression)ExpressionUtils.replacePolyadicWithParent((PsiExpression)token.getParent(), replacement);
|
||||
if (replacedExpression != null) {
|
||||
PsiElement leftOperand = replacedExpression.findElementAt(splitIdx);
|
||||
PsiExpression[] operands = replacedExpression.getOperands();
|
||||
int idx = ArrayUtil.find(operands, leftOperand);
|
||||
if (idx < operands.length - 1) {
|
||||
PsiJavaToken tokenBeforeOperand = replacedExpression.getTokenBeforeOperand(operands[idx + 1]);
|
||||
if (tokenBeforeOperand != null) {
|
||||
editor.getCaretModel().moveToOffset(tokenBeforeOperand.getTextOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PsiPolyadicExpression replaced = (PsiPolyadicExpression)token.getParent().replace(replacement);
|
||||
PsiExpression[] operands = replaced.getOperands();
|
||||
assert operands.length > 1;
|
||||
editor.getCaretModel().moveToOffset(Objects.requireNonNull(replaced.getTokenBeforeOperand(operands[1])).getTextOffset());
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return getFamilyName();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Split text block";
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
String s = <spot>"""
|
||||
Hello, """ + """
|
||||
world!
|
||||
"""</spot>;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
String s = <spot>"""
|
||||
Hello, <caret>world!
|
||||
"""</spot>;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention allows to break text block into 2 parts at caret position.
|
||||
</body>
|
||||
</html>
|
||||
+34
-18
@@ -159,25 +159,10 @@ public class PsiLiteralExpressionImpl
|
||||
}
|
||||
|
||||
private String getTextBlockText() {
|
||||
String rawText = getText();
|
||||
if (rawText.length() < 7 || !rawText.endsWith("\"\"\"")) return null;
|
||||
int start = 3;
|
||||
while (true) {
|
||||
char c = rawText.charAt(start++);
|
||||
if (c == '\n') break;
|
||||
if (!Character.isWhitespace(c) || start == rawText.length()) return null;
|
||||
}
|
||||
String innerText = rawText.substring(start, rawText.length() - 3);
|
||||
String[] lines = StringUtil.splitByLinesDontTrim(innerText);
|
||||
String[] lines = getTextBlockLines();
|
||||
if (lines == null) return null;
|
||||
|
||||
int prefix = Integer.MAX_VALUE;
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
String line = lines[i];
|
||||
int indent = 0;
|
||||
while (indent < line.length() && Character.isWhitespace(line.charAt(indent))) indent++;
|
||||
if (indent == line.length() && i < lines.length - 1) lines[i] = "";
|
||||
else if (indent < prefix) prefix = indent;
|
||||
}
|
||||
int prefix = getTextBlockIndent(lines);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
@@ -192,6 +177,37 @@ public class PsiLiteralExpressionImpl
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getTextBlockIndent() {
|
||||
String[] lines = getTextBlockLines();
|
||||
if (lines == null) return -1;
|
||||
return getTextBlockIndent(lines);
|
||||
}
|
||||
|
||||
private static int getTextBlockIndent(String[] lines) {
|
||||
int prefix = Integer.MAX_VALUE;
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
String line = lines[i];
|
||||
int indent = 0;
|
||||
while (indent < line.length() && Character.isWhitespace(line.charAt(indent))) indent++;
|
||||
if (indent == line.length() && i < lines.length - 1) lines[i] = "";
|
||||
else if (indent < prefix) prefix = indent;
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
private String[] getTextBlockLines() {
|
||||
String rawText = getText();
|
||||
if (rawText.length() < 7 || !rawText.endsWith("\"\"\"")) return null;
|
||||
int start = 3;
|
||||
while (true) {
|
||||
char c = rawText.charAt(start++);
|
||||
if (c == '\n') break;
|
||||
if (!Character.isWhitespace(c) || start == rawText.length()) return null;
|
||||
}
|
||||
String innerText = rawText.substring(start, rawText.length() - 3);
|
||||
return StringUtil.splitByLinesDontTrim(innerText);
|
||||
}
|
||||
|
||||
public String getRawString() {
|
||||
return StringUtil.nullize(StringUtil.trimLeading(StringUtil.trimTrailing(getCanonicalText(), '`'), '`'));
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class MyTest {
|
||||
String text = """
|
||||
Lorem
|
||||
ipsum""" <caret>+ " " + """
|
||||
dolor sit amet
|
||||
""";
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class MyTest {
|
||||
String text = """
|
||||
Lorem
|
||||
ipsum<caret> dolor sit amet
|
||||
""";
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class MyTest {
|
||||
String text = """
|
||||
Lorem ipsum""" <caret>+ " " + """
|
||||
dolor sit amet
|
||||
""";
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class MyTest {
|
||||
String text = """
|
||||
Lorem ipsum <caret>dolor sit amet
|
||||
""";
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// 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.daemon;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LightAdvTextBlocksTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/advTextBlock";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_13;
|
||||
}
|
||||
|
||||
|
||||
public void testSplitTextBlockOnWhitespace() { doTestSplitIntention(); }
|
||||
public void testSplitTextBlock() { doTestSplitIntention(); }
|
||||
|
||||
private void doTestSplitIntention() {
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
List<IntentionAction> actions = myFixture.filterAvailableIntentions("Split text block");
|
||||
assertNotEmpty(actions);
|
||||
myFixture.launchAction(actions.get(0));
|
||||
myFixture.checkResultByFile(getTestName(false) + ".after.java");
|
||||
}
|
||||
|
||||
private void performPaste() {
|
||||
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_PASTE);
|
||||
}
|
||||
|
||||
private void performCopy() {
|
||||
myFixture.performEditorAction(IdeActions.ACTION_EDITOR_COPY);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user