mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
determine content range of text block correctly on pasting in literal (IDEA-CR-50963)
GitOrigin-RevId: aca891bae186771688369389803a7b47da165f91
This commit is contained in:
committed by
intellij-monorepo-bot
parent
ed264d14d2
commit
1c31987860
+16
-31
@@ -164,28 +164,18 @@ public class StringLiteralCopyPasteProcessor implements CopyPastePreProcessor {
|
||||
@Nullable
|
||||
protected PsiElement findLiteralTokenType(PsiFile file, int selectionStart, int selectionEnd) {
|
||||
final PsiElement elementAtSelectionStart = file.findElementAt(selectionStart);
|
||||
if (elementAtSelectionStart == null) {
|
||||
final PsiElement elementAtSelectionEnd = file.findElementAt(selectionEnd);
|
||||
if (elementAtSelectionStart == null || elementAtSelectionEnd == null ||
|
||||
elementAtSelectionEnd.getNode().getElementType() != elementAtSelectionStart.getNode().getElementType()) {
|
||||
return null;
|
||||
}
|
||||
final boolean isTextBlock = isTextBlock(elementAtSelectionStart);
|
||||
if (!isStringLiteral(elementAtSelectionStart) && !isCharLiteral(elementAtSelectionStart) && !isTextBlock) {
|
||||
if (!isStringLiteral(elementAtSelectionStart) && !isCharLiteral(elementAtSelectionStart) && !isTextBlock(elementAtSelectionStart)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final TextRange range = elementAtSelectionStart.getTextRange();
|
||||
final TextRange textRange = isTextBlock ? new TextRange(range.getStartOffset() + 3, range.getEndOffset() - 2) : range;
|
||||
if (textRange.getEndOffset() < selectionEnd) {
|
||||
final PsiElement elementAtSelectionEnd = file.findElementAt(selectionEnd);
|
||||
if (elementAtSelectionEnd == null) {
|
||||
return null;
|
||||
}
|
||||
if (elementAtSelectionEnd.getNode().getElementType() == elementAtSelectionStart.getNode().getElementType() &&
|
||||
elementAtSelectionEnd.getTextRange().getStartOffset() < selectionEnd) {
|
||||
return elementAtSelectionStart;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectionStart <= textRange.getStartOffset() || selectionEnd >= textRange.getEndOffset()) {
|
||||
final TextRange startTextRange = getEscapedRange(elementAtSelectionStart);
|
||||
final TextRange endTextRange = getEscapedRange(elementAtSelectionEnd);
|
||||
if (startTextRange == null || endTextRange == null ||
|
||||
!startTextRange.containsOffset(selectionStart) || !endTextRange.containsOffset(selectionEnd)) {
|
||||
return null;
|
||||
}
|
||||
return elementAtSelectionStart;
|
||||
@@ -208,18 +198,10 @@ public class StringLiteralCopyPasteProcessor implements CopyPastePreProcessor {
|
||||
|
||||
@Nullable
|
||||
protected TextRange getEscapedRange(@NotNull PsiElement token) {
|
||||
if (isCharLiteral(token) || isStringLiteral(token)) {
|
||||
TextRange tokenRange = token.getTextRange();
|
||||
return new TextRange(tokenRange.getStartOffset() + 1, tokenRange.getEndOffset() - 1); // Excluding String/char literal quotes
|
||||
}
|
||||
else if (isTextBlock(token)) {
|
||||
PsiElement parent = token.getParent();
|
||||
if (parent instanceof PsiLiteralExpression && ((PsiLiteralExpression)parent).getValue() != null) {
|
||||
TextRange rangeInParent = StringLiteralManipulator.getValueRange((PsiLiteralExpression)parent);
|
||||
return rangeInParent.shiftRight(parent.getTextRange().getStartOffset());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
PsiElement parent = token.getParent();
|
||||
if (!(parent instanceof PsiLiteralExpression)) return null;
|
||||
final TextRange valueTextRange = StringLiteralManipulator.getValueRange((PsiLiteralExpression)token.getParent());
|
||||
return valueTextRange.shiftRight(token.getTextRange().getStartOffset());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -236,9 +218,12 @@ public class StringLiteralCopyPasteProcessor implements CopyPastePreProcessor {
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
buffer.append(PsiLiteralUtil.escapeTextBlockCharacters(lines[i], i == 0 && escapeStartQuote, i == lines.length - 1 && escapeEndQuote));
|
||||
if (i < lines.length - 1) {
|
||||
buffer.append("\n");
|
||||
buffer.append('\n');
|
||||
}
|
||||
}
|
||||
if (StringUtil.endsWithChar(text, '\n')) {
|
||||
buffer.append('\n');
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
||||
|
||||
+16
-2
@@ -45,8 +45,11 @@ public class StringLiteralManipulator extends AbstractElementManipulator<PsiLite
|
||||
// avoid calling getValue(): it allocates new string, it returns null for invalid escapes
|
||||
IElementType type = ((PsiLiteralExpressionImpl)element).getLiteralElementType();
|
||||
if (type == JavaTokenType.TEXT_BLOCK_LITERAL) {
|
||||
int startOffset = element.getValue() == null ? -1 : element.getText().indexOf('\n');
|
||||
return startOffset < 0 ? TextRange.from(0, length) : new TextRange(startOffset + 1, Math.max(startOffset + 1, length - 3));
|
||||
final String text = element.getText();
|
||||
int startOffset = findBlockStart(text);
|
||||
return startOffset < 0
|
||||
? new TextRange(0, length)
|
||||
: new TextRange(startOffset, length - (text.endsWith("\"\"\"") ? 3 : 0));
|
||||
}
|
||||
isQuoted = type == JavaTokenType.STRING_LITERAL || type == JavaTokenType.CHARACTER_LITERAL;
|
||||
}
|
||||
@@ -56,4 +59,15 @@ public class StringLiteralManipulator extends AbstractElementManipulator<PsiLite
|
||||
}
|
||||
return isQuoted ? new TextRange(1, Math.max(1, length - 1)) : TextRange.from(0, length);
|
||||
}
|
||||
|
||||
private static int findBlockStart(String text) {
|
||||
if (!text.startsWith("\"\"\"")) return -1;
|
||||
final int length = text.length();
|
||||
for (int i = 3; i < length; i++) {
|
||||
final char c = text.charAt(i);
|
||||
if (c == '\n') return i + 1;
|
||||
if (!Character.isWhitespace(c)) return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
class C {
|
||||
String empty = """
|
||||
\a\\""";
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class C {
|
||||
String empty = """
|
||||
\a<caret>""";
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
class C {
|
||||
String empty = """ \
|
||||
b\ad esc\ape """;
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
class C {
|
||||
String empty = """ <caret>
|
||||
b\ad esc\ape """;
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class C {
|
||||
String empty = """
|
||||
|
||||
|
||||
""";
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class C {
|
||||
String empty = """
|
||||
<caret>""";
|
||||
}
|
||||
+9
-9
@@ -22,17 +22,17 @@ class JavaTextBlocksHighlightingTest : LightJavaCodeInsightFixtureTestCase() {
|
||||
myFixture.checkHighlighting()
|
||||
}
|
||||
|
||||
fun testEscapeQuotes() {
|
||||
doTestPaste("\"\"\"\ntarget\"\"\"")
|
||||
}
|
||||
fun testEscapeQuotes() = doTestPaste("\"\"\"\ntarget\"\"\"")
|
||||
|
||||
fun testEscapeQuotes2() {
|
||||
doTestPaste("\"\ntarget\"")
|
||||
}
|
||||
fun testEscapeQuotes2() = doTestPaste("\"\ntarget\"")
|
||||
|
||||
fun testNoEscapeWhenNotInTextBlockContent() {
|
||||
doTestPaste("\\");
|
||||
}
|
||||
fun testNoEscapeWhenNotInTextBlockContent() = doTestPaste("\\")
|
||||
|
||||
fun testNoEscapeWhenInTextBlockPrefix() = doTestPaste("\\")
|
||||
|
||||
fun testPasteNewline() = doTestPaste("\n\n")
|
||||
|
||||
fun testBadEscape() = doTestPaste("\\");
|
||||
|
||||
private fun doTestPaste(textToPaste: String) {
|
||||
myFixture.configureByText("plain.txt", "<selection>$textToPaste</selection>")
|
||||
|
||||
Reference in New Issue
Block a user