Java: fix selection behaviour for String Templates (IDEA-333906)

GitOrigin-RevId: 0727e9c5b4a56d752bc38d7fc6fa22ca676dd9ba
This commit is contained in:
Bas Leijdekkers
2023-10-31 17:17:28 +01:00
committed by intellij-monorepo-bot
parent 8925dcb722
commit 2ebaab02d1
28 changed files with 281 additions and 43 deletions

View File

@@ -7,60 +7,80 @@ import com.intellij.lexer.StringLiteralLexer;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.BasicLiteralUtil;
import com.intellij.psi.JavaTokenType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
import com.intellij.psi.impl.source.BasicElementTypes;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import static com.intellij.psi.impl.source.BasicElementTypes.BASIC_STRING_LITERALS;
public class LiteralSelectioner extends AbstractBasicBackBasicSelectioner {
@Override
public boolean canSelect(@NotNull PsiElement e) {
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_STRING_LITERALS);
IElementType type = e.getNode().getElementType();
return BasicElementTypes.BASIC_STRING_LITERALS.contains(type) ||
BasicElementTypes.BASIC_STRING_TEMPLATE_FRAGMENTS.contains(type);
}
@Override
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
List<TextRange> result = super.select(e, editorText, cursorOffset, editor);
if (result == null) {
return null;
}
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
if (node == null) {
return null;
}
boolean textBlock = BasicJavaAstTreeUtil.isTextBlock(node);
StringLiteralLexer lexer = textBlock
? new StringLiteralLexer(StringLiteralLexer.NO_QUOTE_CHAR, JavaTokenType.TEXT_BLOCK_LITERAL, true, "s{")
: new StringLiteralLexer('"', JavaTokenType.STRING_LITERAL);
TextRange range = node.getTextRange();
SelectWordUtil.addWordHonoringEscapeSequences(editorText, range, cursorOffset, lexer, result);
if (textBlock) {
int contentStart = StringUtil.indexOf(editorText, '\n', range.getStartOffset());
if (contentStart == -1) return result;
int start = contentStart + 1;
int end = range.getEndOffset();
end -= StringUtil.endsWith(editorText, start, end, "\"\"\"") ? 4 : 1;
for (; end >= start; end--) {
char c = editorText.charAt(end);
if (c == '\n' || !Character.isWhitespace(c)) {
end += 1;
break;
}
}
if (start < end) result.add(new TextRange(start, end));
}
else {
int endOffset = editorText.charAt(range.getEndOffset() - 1) == '"'
? range.getEndOffset() - 1
: range.getEndOffset();
result.add(new TextRange(range.getStartOffset() + 1, endOffset));
}
if (result == null) return null;
ASTNode node = e.getNode();
StringLiteralLexer lexer = new StringLiteralLexer(StringLiteralLexer.NO_QUOTE_CHAR, node.getElementType(), true, "s{");
SelectWordUtil.addWordHonoringEscapeSequences(editorText, node.getTextRange(), cursorOffset, lexer, result);
result.add(getContentRange(node, editorText));
return result;
}
}
private static TextRange getContentRange(ASTNode node, @NotNull CharSequence text) {
final IElementType tokenType = node.getElementType();
final TextRange range = node.getTextRange();
if (tokenType == JavaTokenType.STRING_TEMPLATE_BEGIN || tokenType == JavaTokenType.STRING_TEMPLATE_MID ||
tokenType == JavaTokenType.TEXT_BLOCK_TEMPLATE_MID) {
return new TextRange(range.getStartOffset() + 1, range.getEndOffset() - 2);
}
else if (tokenType == JavaTokenType.STRING_TEMPLATE_END || tokenType == JavaTokenType.STRING_LITERAL) {
int end = text.charAt(range.getEndOffset() - 1) == '"'
? range.getEndOffset() - 1
: range.getEndOffset();
return new TextRange(range.getStartOffset() + 1, end);
}
else if (tokenType == JavaTokenType.TEXT_BLOCK_TEMPLATE_BEGIN || tokenType == JavaTokenType.TEXT_BLOCK_TEMPLATE_END ||
tokenType == JavaTokenType.TEXT_BLOCK_LITERAL) {
int start;
if (tokenType == JavaTokenType.TEXT_BLOCK_TEMPLATE_END) {
start = range.getStartOffset() + 1;
}
else {
start = range.getStartOffset() + 3;
while (BasicLiteralUtil.isTextBlockWhiteSpace(text.charAt(start))) start++;
if (text.charAt(start) == '\n') start++;
}
int end;
if (tokenType == JavaTokenType.TEXT_BLOCK_TEMPLATE_BEGIN) {
end = range.getEndOffset() - 2;
}
else {
end = range.getEndOffset();
end -= StringUtil.endsWith(text, start, end, "\"\"\"") ? 4 : 1;
for (; end >= start; end--) {
char c = text.charAt(end);
if (c == '\n' || !Character.isWhitespace(c)) {
end += 1;
break;
}
}
}
return new TextRange(start, end);
}
else {
throw new IllegalArgumentException();
}
}
}