mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 06:05:01 +07:00
[java-rd] IDEA-322563 Improve editing experience in Remote Dev for Java
- editor actions GitOrigin-RevId: e774f42e72be12b613a9c300dd988589048bd483
This commit is contained in:
committed by
intellij-monorepo-bot
parent
e23e60a0eb
commit
d2af2f9736
+348
@@ -0,0 +1,348 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorModificationUtil;
|
||||
import com.intellij.openapi.editor.EditorModificationUtilEx;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
|
||||
public abstract class AbstractBasicJavaTypedHandler extends TypedHandlerDelegate {
|
||||
private boolean myJavaLTTyped;
|
||||
|
||||
protected AbstractBasicJavaTypedHandler() {
|
||||
}
|
||||
|
||||
protected abstract boolean isJavaFile(@NotNull PsiFile file);
|
||||
|
||||
protected abstract boolean isJspFile(@NotNull PsiFile file);
|
||||
|
||||
protected abstract void autoPopupMemberLookup(@NotNull Project project, @NotNull Editor editor);
|
||||
|
||||
protected abstract void autoPopupJavadocLookup(@NotNull final Project project, @NotNull final Editor editor);
|
||||
|
||||
protected abstract boolean isLanguageLevel5OrHigher(@NotNull PsiFile file);
|
||||
|
||||
@NotNull
|
||||
protected abstract Result processWhileAndIfStatementBody(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file);
|
||||
|
||||
|
||||
/**
|
||||
* Automatically inserts parentheses if != or == was typed after a&b, a|b or a^b where a and b are numbers.
|
||||
*
|
||||
* @return true if the '=' char was processed
|
||||
*/
|
||||
public abstract boolean handleEquality(Project project, Editor editor, PsiFile file, int offsetBefore);
|
||||
|
||||
/**
|
||||
* Automatically insert parentheses around the ?: when necessary.
|
||||
*
|
||||
* @return true if question mark was handled
|
||||
*/
|
||||
protected abstract boolean handleQuestionMark(Project project, Editor editor, PsiFile file, int offsetBefore);
|
||||
|
||||
protected abstract boolean handleAnnotationParameter(Project project, @NotNull Editor editor, @NotNull PsiFile file);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Result beforeCharTyped(final char c,
|
||||
@NotNull final Project project,
|
||||
@NotNull final Editor editor,
|
||||
@NotNull final PsiFile file,
|
||||
@NotNull final FileType fileType) {
|
||||
if (!isJavaFile(file)) return Result.CONTINUE;
|
||||
|
||||
if (c == '@') {
|
||||
autoPopupJavadocLookup(project, editor);
|
||||
}
|
||||
else if (c == '#' || c == '.') {
|
||||
autoPopupMemberLookup(project, editor);
|
||||
}
|
||||
|
||||
int offsetBefore = editor.getCaretModel().getOffset();
|
||||
|
||||
//important to calculate before inserting charTyped
|
||||
myJavaLTTyped = '<' == c &&
|
||||
!isJspFile(file) &&
|
||||
CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET &&
|
||||
isLanguageLevel5OrHigher(file) &&
|
||||
TypedHandlerUtil.isAfterClassLikeIdentifierOrDot(offsetBefore, editor, JavaTokenType.DOT, JavaTokenType.IDENTIFIER,
|
||||
true);
|
||||
|
||||
if ('>' == c) {
|
||||
if (!isJspFile(file) && CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && isLanguageLevel5OrHigher(file)) {
|
||||
if (TypedHandlerUtil.handleGenericGT(editor, JavaTokenType.LT, JavaTokenType.GT, JavaTypingTokenSets.INVALID_INSIDE_REFERENCE)) {
|
||||
return Result.STOP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '?') {
|
||||
if (handleQuestionMark(project, editor, file, offsetBefore)) {
|
||||
return Result.STOP;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '=') {
|
||||
if (handleEquality(project, editor, file, offsetBefore)) {
|
||||
return Result.STOP;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == ';') {
|
||||
if (handleSemicolon(project, editor, file, fileType)) return Result.STOP;
|
||||
}
|
||||
if (fileType instanceof JavaFileType && c == '{') {
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
if (offset == 0) {
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
|
||||
HighlighterIterator iterator = editor.getHighlighter().createIterator(offset - 1);
|
||||
while (!iterator.atEnd() && iterator.getTokenType() == TokenType.WHITE_SPACE) {
|
||||
iterator.retreat();
|
||||
}
|
||||
if (iterator.atEnd() || iterator.getTokenType() == JavaTokenType.RBRACKET || iterator.getTokenType() == JavaTokenType.EQ) {
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
Document doc = editor.getDocument();
|
||||
PsiDocumentManager.getInstance(project).commitDocument(doc);
|
||||
final PsiElement leaf = file.findElementAt(offset);
|
||||
if (BasicJavaAstTreeUtil.getParentOfType(leaf, BASIC_ARRAY_INITIALIZER_EXPRESSION, false,
|
||||
BasicJavaTokenSet.orSet(BasicJavaTokenSet.create(BASIC_CODE_BLOCK), MEMBER_SET)) != null) {
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
PsiElement st = leaf != null ? leaf.getParent() : null;
|
||||
PsiElement prev = offset > 1 ? file.findElementAt(offset - 1) : null;
|
||||
if (CodeInsightSettings.getInstance().AUTOINSERT_PAIR_BRACKET && isRparenth(leaf) &&
|
||||
st != null &&
|
||||
(BasicJavaAstTreeUtil.is(st.getNode(), BASIC_WHILE_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(st.getNode(), BASIC_IF_STATEMENT)) &&
|
||||
shouldInsertStatementBody(st, doc, prev)) {
|
||||
return processWhileAndIfStatementBody(project, editor, file);
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.getParentOfType(leaf, BASIC_CODE_BLOCK, false, MEMBER_SET) != null &&
|
||||
!shouldInsertPairedBrace(leaf)) {
|
||||
EditorModificationUtilEx.insertStringAtCaret(editor, "{");
|
||||
TypedHandler.indentOpenedBrace(project, editor);
|
||||
return Result.STOP; // use case: manually wrapping part of method's code in 'if', 'while', etc
|
||||
}
|
||||
}
|
||||
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
|
||||
private static boolean shouldInsertPairedBrace(@NotNull PsiElement leaf) {
|
||||
PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(leaf);
|
||||
// lambda
|
||||
if (prevLeaf != null && prevLeaf.getNode().getElementType() == JavaTokenType.ARROW) return true;
|
||||
// anonymous class
|
||||
BasicJavaTokenSet stopAt = BasicJavaTokenSet.orSet(MEMBER_SET, BasicJavaTokenSet.create(BASIC_CODE_BLOCK));
|
||||
if (BasicJavaAstTreeUtil.getParentOfType(prevLeaf, BASIC_NEW_EXPRESSION, true, stopAt) != null) return true;
|
||||
// local class
|
||||
if (prevLeaf != null && prevLeaf.getParent() != null && BasicJavaAstTreeUtil.is(prevLeaf.getNode(), JavaTokenType.IDENTIFIER) &&
|
||||
BasicJavaAstTreeUtil.is(prevLeaf.getParent().getNode(), CLASS_SET)) {
|
||||
return true;
|
||||
}
|
||||
// local record
|
||||
if (prevLeaf != null && prevLeaf.getParent() != null && prevLeaf.getNode().getElementType() == JavaTokenType.RPARENTH &&
|
||||
BasicJavaAstTreeUtil.is(prevLeaf.getParent().getNode(), BASIC_RECORD_HEADER)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean shouldInsertStatementBody(@NotNull PsiElement statement, @NotNull Document doc, @Nullable PsiElement prev) {
|
||||
|
||||
ASTNode block;
|
||||
ASTNode astNodeStatement = statement.getNode();
|
||||
if (BasicJavaAstTreeUtil.is(astNodeStatement, BASIC_WHILE_STATEMENT)) {
|
||||
block = BasicJavaAstTreeUtil.getBlock(astNodeStatement);
|
||||
}
|
||||
else {
|
||||
block = BasicJavaAstTreeUtil.getThenBranch(astNodeStatement);
|
||||
}
|
||||
ASTNode condition = BasicJavaAstTreeUtil.findChildByType(astNodeStatement, EXPRESSION_SET);
|
||||
ASTNode latestExpression = BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toNode(prev), EXPRESSION_SET);
|
||||
if (BasicJavaAstTreeUtil.is(latestExpression, BASIC_NEW_EXPRESSION) &&
|
||||
BasicJavaAstTreeUtil.getAnonymousClass(latestExpression) == null) {
|
||||
return false;
|
||||
}
|
||||
return !(BasicJavaAstTreeUtil.is(block, BASIC_BLOCK_STATEMENT)) &&
|
||||
(block == null || startLine(doc, block) != startLine(doc, astNodeStatement) || condition == null);
|
||||
}
|
||||
|
||||
private static boolean isRparenth(@Nullable PsiElement leaf) {
|
||||
if (leaf == null) return false;
|
||||
if (leaf.getNode().getElementType() == JavaTokenType.RPARENTH) return true;
|
||||
PsiElement next = PsiTreeUtil.nextVisibleLeaf(leaf);
|
||||
if (next == null) return false;
|
||||
return next.getNode().getElementType() == JavaTokenType.RPARENTH;
|
||||
}
|
||||
|
||||
private static int startLine(@NotNull Document doc, @NotNull ASTNode astNode) {
|
||||
return doc.getLineNumber(astNode.getTextRange().getStartOffset());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Result charTyped(final char c, @NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) {
|
||||
if (!(isJavaFile(file))) return Result.CONTINUE;
|
||||
|
||||
if (myJavaLTTyped) {
|
||||
myJavaLTTyped = false;
|
||||
TypedHandlerUtil.handleAfterGenericLT(editor, JavaTokenType.LT, JavaTokenType.GT, JavaTypingTokenSets.INVALID_INSIDE_REFERENCE);
|
||||
return Result.STOP;
|
||||
}
|
||||
else if (c == ':') {
|
||||
if (autoIndentCase(editor, project, file)) {
|
||||
return Result.STOP;
|
||||
}
|
||||
}
|
||||
else if (c == ',' && handleAnnotationParameter(project, editor, file)) {
|
||||
return Result.STOP;
|
||||
}
|
||||
else if (c == '.') {
|
||||
if (handleDotTyped(project, editor, file)) {
|
||||
return Result.STOP;
|
||||
}
|
||||
}
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
|
||||
private static boolean handleDotTyped(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
int offset = editor.getCaretModel().getOffset() - 1;
|
||||
if (offset >= 0) {
|
||||
Document document = editor.getDocument();
|
||||
int line = document.getLineNumber(offset);
|
||||
int lineStart = document.getLineStartOffset(line);
|
||||
if (StringUtil.isEmptyOrSpaces(document.getCharsSequence().subSequence(lineStart, offset))) {
|
||||
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
|
||||
CodeStyleManager.getInstance(project).adjustLineIndent(file, offset);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean handleSemicolon(@NotNull Project project,
|
||||
@NotNull Editor editor,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull FileType fileType) {
|
||||
if (!(fileType instanceof JavaFileType)) return false;
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
if (offset == editor.getDocument().getTextLength()) return false;
|
||||
|
||||
if (moveSemicolonAtRParen(project, editor, file, offset)) return true;
|
||||
|
||||
char charAt = editor.getDocument().getCharsSequence().charAt(offset);
|
||||
if (charAt != ';') return false;
|
||||
|
||||
HighlighterIterator hi = editor.getHighlighter().createIterator(offset);
|
||||
if (hi.atEnd() || hi.getTokenType() != JavaTokenType.SEMICOLON) return false;
|
||||
|
||||
EditorModificationUtil.moveCaretRelatively(editor, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean moveSemicolonAtRParen(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file, int caretOffset) {
|
||||
if (!Registry.is("editor.move.semicolon.after.paren")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().assertWriteAccessAllowed();
|
||||
|
||||
// Note, this feature may be rewritten using only lexer if needed.
|
||||
// In that case accuracy will not be 100%, but good enough.
|
||||
|
||||
HighlighterIterator it = editor.getHighlighter().createIterator(caretOffset);
|
||||
int afterLastParenOffset = -1;
|
||||
|
||||
while (!it.atEnd()) {
|
||||
if (isAtLineEnd(it)) {
|
||||
break;
|
||||
}
|
||||
else if (it.getTokenType() == JavaTokenType.RBRACE) {
|
||||
break;
|
||||
}
|
||||
else if (it.getTokenType() == JavaTokenType.RPARENTH) {
|
||||
afterLastParenOffset = it.getEnd();
|
||||
}
|
||||
else if (it.getTokenType() != TokenType.WHITE_SPACE) {
|
||||
// Other tokens are not permitted
|
||||
return false;
|
||||
}
|
||||
it.advance();
|
||||
}
|
||||
|
||||
if (!it.atEnd() && afterLastParenOffset >= 0 && afterLastParenOffset >= caretOffset) {
|
||||
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
|
||||
PsiElement curElement = file.findElementAt(caretOffset);
|
||||
ASTNode curStmt = BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toNode(curElement), STATEMENT_SET);
|
||||
if (curStmt != null) {
|
||||
if (BasicJavaAstTreeUtil.is(curStmt, BASIC_TRY_STATEMENT)) {
|
||||
// try-with-resources can contain semicolons inside
|
||||
return false;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(curStmt, BASIC_FOR_STATEMENT)) {
|
||||
// for loop can have semicolons inside
|
||||
return false;
|
||||
}
|
||||
// It may worth to check if the error element is about expecting semicolon
|
||||
PsiElement curPsiElement = BasicJavaAstTreeUtil.toPsi(curStmt);
|
||||
if (curPsiElement != null && PsiTreeUtil.getDeepestLast(curPsiElement) instanceof PsiErrorElement) {
|
||||
int stmtEndOffset = curStmt.getTextRange().getEndOffset();
|
||||
if (stmtEndOffset == afterLastParenOffset || stmtEndOffset == it.getStart()) {
|
||||
editor.getDocument().insertString(stmtEndOffset, ";");
|
||||
editor.getCaretModel().moveToOffset(stmtEndOffset + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isAtLineEnd(HighlighterIterator it) {
|
||||
if (it.getTokenType() == TokenType.WHITE_SPACE) {
|
||||
CharSequence tokenText = it.getDocument().getImmutableCharSequence().subSequence(it.getStart(), it.getEnd());
|
||||
return CharArrayUtil.containLineBreaks(tokenText);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean autoIndentCase(Editor editor, Project project, PsiFile file) {
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
|
||||
PsiElement currElement = file.findElementAt(offset - 1);
|
||||
if (currElement != null) {
|
||||
PsiElement parent = currElement.getParent();
|
||||
if (BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(parent), BASIC_SWITCH_LABEL_STATEMENT)) {
|
||||
CodeStyleManager.getInstance(project).adjustLineIndent(file, parent.getTextOffset());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightSettings;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
|
||||
import static com.intellij.util.text.CharArrayUtil.containsOnlyWhiteSpaces;
|
||||
|
||||
/**
|
||||
* Advises typing in javadoc if necessary.
|
||||
*/
|
||||
public abstract class AbstractBasicJavadocTypedHandler extends TypedHandlerDelegate {
|
||||
|
||||
private static final char START_TAG_SYMBOL = '<';
|
||||
private static final char CLOSE_TAG_SYMBOL = '>';
|
||||
private static final char SLASH = '/';
|
||||
private static final String COMMENT_PREFIX = "!--";
|
||||
|
||||
protected AbstractBasicJavadocTypedHandler() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Result charTyped(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
if (isJavaFile(file)) {
|
||||
if (!insertClosingTagIfNecessary(c, project, editor, file)) {
|
||||
adjustStartTagIndent(c, editor, file);
|
||||
}
|
||||
}
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
|
||||
public abstract boolean isJavaFile(@Nullable PsiFile file);
|
||||
|
||||
private static void adjustStartTagIndent(char c, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
if (c == '@') {
|
||||
final int offset = editor.getCaretModel().getOffset();
|
||||
PsiElement currElement = file.findElementAt(offset);
|
||||
if (currElement instanceof PsiWhiteSpace) {
|
||||
PsiElement prev = currElement.getPrevSibling();
|
||||
if (prev != null && prev.getNode().getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
|
||||
editor.getDocument().replaceString(currElement.getTextRange().getStartOffset(), offset - 1, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if it's necessary to insert closing tag on typed character.
|
||||
*
|
||||
* @param c typed symbol
|
||||
* @param project current project
|
||||
* @param editor current editor
|
||||
* @param file current file
|
||||
* @return {@code true} if closing tag is inserted; {@code false} otherwise
|
||||
*/
|
||||
private boolean insertClosingTagIfNecessary(char c, @NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
if (c != CLOSE_TAG_SYMBOL || !CodeInsightSettings.getInstance().JAVADOC_GENERATE_CLOSING_TAG) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments();
|
||||
|
||||
if (!isAppropriatePlace(editor, file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Inspect symbols to the left of the current caret position, insert closing tag only if valid tag is just typed
|
||||
// (e.g. don't insert anything on single '>' symbol typing).
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
Document document = editor.getDocument();
|
||||
String tagName = getTagName(document.getText(), offset);
|
||||
if (tagName == null || isSingleHtmlTag(tagName) || tagName.startsWith(COMMENT_PREFIX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
document.insertString(offset, String.valueOf(START_TAG_SYMBOL) + SLASH + tagName + CLOSE_TAG_SYMBOL);
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract boolean isSingleHtmlTag(@NotNull String tagName);
|
||||
|
||||
/**
|
||||
* Tries to derive start tag name assuming that given offset points to position just after {@code '>'} symbol.
|
||||
* <p/>
|
||||
* Is expected to return {@code null} when offset is not located just after start tag, e.g. the following situations:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>standalone {@code '>'} symbol (surrounded by white spaces);</li>
|
||||
* <li>after end tag {@code <mytag><mytag>[caret]};</li>
|
||||
* <li>after empty element tag {@code <p/>[caret]};</li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
*
|
||||
* @param text target text
|
||||
* @param afterTagOffset offset that points after
|
||||
* @return tag name if the one is parsed; {@code null} otherwise
|
||||
*/
|
||||
@Nullable
|
||||
public static String getTagName(@NotNull CharSequence text, int afterTagOffset) {
|
||||
if (afterTagOffset > text.length()) {
|
||||
return null;
|
||||
}
|
||||
int endOffset = afterTagOffset - 1;
|
||||
|
||||
// Check empty element like <p/>
|
||||
if (endOffset > 0 && text.charAt(endOffset - 1) == SLASH) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = endOffset - 1; i >= 0; i--) {
|
||||
char c = text.charAt(i);
|
||||
switch (c) {
|
||||
case '\n' -> {
|
||||
return null;
|
||||
}
|
||||
case CLOSE_TAG_SYMBOL -> {
|
||||
return null;
|
||||
}
|
||||
case START_TAG_SYMBOL -> {
|
||||
if (text.charAt(i + 1) == SLASH) {
|
||||
// Handle situation like <tag></tag>[offset].
|
||||
return null;
|
||||
}
|
||||
return text.subSequence(i + 1, endOffset).toString();
|
||||
}
|
||||
|
||||
// There is a possible case that opening tag has attributes, e.g. <a href='bla-bla-bla'>[offset]. We want to extract
|
||||
// only tag name then.
|
||||
case ' ', '\t' -> endOffset = i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isAppropriatePlace(Editor editor, PsiFile file) {
|
||||
FileViewProvider provider = file.getViewProvider();
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
|
||||
final PsiElement elementAtCaret;
|
||||
if (offset < editor.getDocument().getTextLength()) {
|
||||
elementAtCaret = provider.findElementAt(offset);
|
||||
}
|
||||
else {
|
||||
elementAtCaret = provider.findElementAt(editor.getDocument().getTextLength() - 1);
|
||||
}
|
||||
|
||||
PsiElement element = elementAtCaret;
|
||||
while (element instanceof PsiWhiteSpace || element != null && containsOnlyWhiteSpaces(element.getText())) {
|
||||
element = element.getPrevSibling();
|
||||
}
|
||||
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
ASTNode astNode = BasicJavaAstTreeUtil.toNode(element);
|
||||
if (BasicJavaAstTreeUtil.is(astNode, DOC_PARAMETER_REF)) {
|
||||
astNode = astNode.getTreeParent();
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(astNode, DOC_TAG, DOC_SNIPPET_TAG, DOC_INLINE_TAG) &&
|
||||
"param".equals(BasicJavaAstTreeUtil.getTagName(astNode)) &&
|
||||
isTypeParamBracketClosedAfterParamTag(astNode, offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// The contents of inline tags is not HTML, so the paired tag completion isn't appropriate there.
|
||||
if (BasicJavaAstTreeUtil.is(astNode, DOC_INLINE_TAG, DOC_SNIPPET_TAG) ||
|
||||
BasicJavaAstTreeUtil.getParentOfType(astNode, BasicJavaTokenSet.create(DOC_INLINE_TAG, DOC_SNIPPET_TAG)) != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ASTNode node = element.getNode();
|
||||
return node != null
|
||||
&& (JavaDocTokenType.ALL_JAVADOC_TOKENS.contains(node.getElementType())
|
||||
|| ALL_JAVADOC_ELEMENTS.contains(node.getElementType()));
|
||||
}
|
||||
|
||||
private static boolean isTypeParamBracketClosedAfterParamTag(ASTNode tag, int bracketOffset) {
|
||||
ASTNode paramToDocument = getDocumentingParameter(tag);
|
||||
if (paramToDocument == null) return false;
|
||||
|
||||
TextRange paramRange = paramToDocument.getTextRange();
|
||||
return paramRange.getEndOffset() == bracketOffset;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ASTNode getDocumentingParameter(@NotNull ASTNode tag) {
|
||||
for (ASTNode element = tag.getFirstChildNode(); element != null; element = element.getTreeNext()) {
|
||||
if (BasicJavaAstTreeUtil.is(element, DOC_PARAMETER_REF)) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorModificationUtilEx;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AutoFormatTypedHandler extends TypedHandlerDelegate {
|
||||
private static boolean myIsEnabledInTests;
|
||||
|
||||
private static final char[] NO_SPACE_AFTER = {
|
||||
'+', '-', '*', '/', '%', '&', '^', '|', '<', '>', '!', '=', ' '
|
||||
};
|
||||
|
||||
private static final List<IElementType> COMPLEX_ASSIGNMENTS = List.of(JavaTokenType.PLUSEQ, JavaTokenType.MINUSEQ,
|
||||
JavaTokenType.ASTERISKEQ, JavaTokenType.DIVEQ,
|
||||
JavaTokenType.PERCEQ,
|
||||
JavaTokenType.ANDEQ, JavaTokenType.XOREQ, JavaTokenType.OREQ,
|
||||
JavaTokenType.LTLTEQ, JavaTokenType.GTGTEQ);
|
||||
|
||||
private static boolean isEnabled(Editor editor) {
|
||||
boolean isEnabled = myIsEnabledInTests && ApplicationManager.getApplication().isUnitTestMode()
|
||||
|| Registry.is("editor.reformat.on.typing");
|
||||
|
||||
if (!isEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Project project = editor.getProject();
|
||||
Language language = null;
|
||||
if (project != null) {
|
||||
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
if (file != null) {
|
||||
language = file.getLanguage();
|
||||
}
|
||||
}
|
||||
|
||||
return language == JavaLanguage.INSTANCE;
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public static void setEnabledInTests(boolean value) {
|
||||
myIsEnabledInTests = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Result beforeCharTyped(char c,
|
||||
@NotNull Project project,
|
||||
@NotNull Editor editor,
|
||||
@NotNull PsiFile file,
|
||||
@NotNull FileType fileType) {
|
||||
if (!isEnabled(editor)) {
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
if (isInsertSpaceAtCaret(editor, c, project)) {
|
||||
EditorModificationUtilEx.insertStringAtCaret(editor, " ");
|
||||
}
|
||||
|
||||
return Result.CONTINUE;
|
||||
}
|
||||
|
||||
private static boolean isInsertSpaceAtCaret(@NotNull Editor editor, char charTyped, @NotNull Project project) {
|
||||
if (!isSpaceAroundAssignment(editor, project)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int caretOffset = editor.getCaretModel().getOffset();
|
||||
CharSequence text = editor.getDocument().getImmutableCharSequence();
|
||||
|
||||
HighlighterIterator lexerIterator = createLexerIterator(editor, caretOffset);
|
||||
if (lexerIterator == null || lexerIterator.getTokenType() == JavaTokenType.STRING_LITERAL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean insertBeforeEq = charTyped == '=' && isInsertSpaceBeforeEq(caretOffset, text);
|
||||
boolean insertAfterEq = caretOffset > 0 && caretOffset - 1 < text.length() && text.charAt(caretOffset - 1) == '='
|
||||
&& isAssignmentOperator(lexerIterator) && isInsertSpaceAfterEq(charTyped);
|
||||
|
||||
return (insertBeforeEq || insertAfterEq);
|
||||
}
|
||||
|
||||
private static boolean isAssignmentOperator(HighlighterIterator iterator) {
|
||||
IElementType type = iterator.getTokenType();
|
||||
if (type == TokenType.WHITE_SPACE) {
|
||||
iterator.retreat();
|
||||
type = iterator.getTokenType();
|
||||
}
|
||||
|
||||
if (COMPLEX_ASSIGNMENTS.contains(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == JavaTokenType.EQ) {
|
||||
iterator.retreat();
|
||||
type = iterator.getTokenType();
|
||||
if (type == JavaTokenType.GT) {
|
||||
iterator.retreat();
|
||||
type = iterator.getTokenType();
|
||||
if (type == JavaTokenType.GT) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
else if (type == TokenType.WHITE_SPACE || type == JavaTokenType.IDENTIFIER) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isInsertSpaceAfterEq(char charTyped) {
|
||||
return charTyped != '=' && charTyped != ' ';
|
||||
}
|
||||
|
||||
private static HighlighterIterator createLexerIterator(Editor editor, int offset) {
|
||||
if (editor.getDocument().getTextLength() == 0) return null;
|
||||
return editor.getHighlighter().createIterator(offset);
|
||||
}
|
||||
|
||||
private static boolean isInsertSpaceBeforeEq(int caretOffset, CharSequence text) {
|
||||
if (caretOffset == 0) return false;
|
||||
char charBefore = text.charAt(caretOffset - 1);
|
||||
|
||||
for (char c : NO_SPACE_AFTER) {
|
||||
if (c == charBefore) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isSpaceAroundAssignment(Editor editor, Project project) {
|
||||
PsiFile file = project == null ? null : PsiUtilBase.getPsiFileInEditor(editor, project);
|
||||
if (file != null) {
|
||||
Language language = file.getLanguage();
|
||||
CodeStyleSettings settings = CodeStyle.getSettings(editor);
|
||||
CommonCodeStyleSettings common = settings.getCommonSettings(language);
|
||||
return common.SPACE_AROUND_ASSIGNMENT_OPERATORS;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright 2000-2023 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.editorActions;
|
||||
|
||||
import com.intellij.codeInsight.definition.AbstractBasicJavaDefinitionService;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.AbstractBasicJavaFile;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JavaBackspaceHandler extends BackspaceHandlerDelegate {
|
||||
private boolean myToDeleteGt;
|
||||
|
||||
@Override
|
||||
public void beforeCharDeleted(char c, @NotNull PsiFile file, @NotNull Editor editor) {
|
||||
myToDeleteGt = c == '<' &&
|
||||
isHigherThan50r(file) &&
|
||||
TypedHandlerUtil.isAfterClassLikeIdentifierOrDot(editor.getCaretModel().getOffset() - 1,
|
||||
editor, JavaTokenType.DOT, JavaTokenType.IDENTIFIER, true);
|
||||
}
|
||||
|
||||
private boolean isHigherThan50r(@Nullable PsiFile file){
|
||||
return file instanceof AbstractBasicJavaFile &&
|
||||
AbstractBasicJavaDefinitionService.getJavaDefinitionService().getLanguageLevel(file).isAtLeast(LanguageLevel.JDK_1_5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean charDeleted(final char c, @NotNull final PsiFile file, @NotNull final Editor editor) {
|
||||
if (c == '<' && myToDeleteGt) {
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
final CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
if (editor.getDocument().getTextLength() <= offset) return false; //virtual space after end of file
|
||||
|
||||
char c1 = chars.charAt(offset);
|
||||
if (c1 != '>') return true;
|
||||
TypedHandlerUtil.handleGenericLTDeletion(editor, offset, JavaTokenType.LT, JavaTokenType.GT, JavaTypingTokenSets.INVALID_INSIDE_REFERENCE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. 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.editorActions;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
|
||||
import com.intellij.core.JavaPsiBundle;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.AbstractBasicJavaFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiErrorElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_EXPRESSION_LIST_STATEMENT;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.EXPRESSION_SET;
|
||||
|
||||
public class JavaEnterAfterUnmatchedBraceHandler extends EnterAfterUnmatchedBraceHandler {
|
||||
|
||||
protected JavaEnterAfterUnmatchedBraceHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(@NotNull PsiFile file, int caretOffset) {
|
||||
return file instanceof AbstractBasicJavaFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int calculateOffsetToInsertClosingBraceInsideElement(PsiElement element) {
|
||||
if (element instanceof PsiErrorElement &&
|
||||
((PsiErrorElement)element).getErrorDescription().equals(JavaPsiBundle.message("else.without.if"))) {
|
||||
return element.getTextRange().getStartOffset();
|
||||
}
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(element);
|
||||
if (BasicJavaAstTreeUtil.is(node, BASIC_EXPRESSION_LIST_STATEMENT)) {
|
||||
final ASTNode list = BasicJavaAstTreeUtil.getExpressionList(node);
|
||||
if (list != null) {
|
||||
final ASTNode firstExpression = BasicJavaAstTreeUtil.findChildByType(list, EXPRESSION_SET);
|
||||
if (firstExpression != null) {
|
||||
return firstExpression.getTextRange().getEndOffset();
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.calculateOffsetToInsertClosingBraceInsideElement(element);
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// Copyright 2000-2023 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.editorActions;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterInStringLiteralHandler;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
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.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_LITERAL_EXPRESSION;
|
||||
|
||||
public class JavaEnterInTextBlockHandler extends EnterInStringLiteralHandler {
|
||||
|
||||
@Override
|
||||
public Result preprocessEnter(@NotNull PsiFile file,
|
||||
@NotNull Editor editor,
|
||||
@NotNull Ref<Integer> caretOffsetRef,
|
||||
@NotNull Ref<Integer> caretAdvanceRef,
|
||||
@NotNull DataContext dataContext,
|
||||
EditorActionHandler originalHandler) {
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
PsiElement textBlock = getTextBlockAt(file, offset);
|
||||
if (textBlock == null) return Result.Continue;
|
||||
int textBlockOffset = textBlock.getTextOffset();
|
||||
String text = textBlock.getText();
|
||||
int offsetInTextBlock = offset - textBlockOffset;
|
||||
boolean isAtFirstLine = !text.substring(0, offsetInTextBlock).contains("\n");
|
||||
if (!isAtFirstLine) return Result.Continue;
|
||||
Document document = editor.getDocument();
|
||||
Project project = textBlock.getProject();
|
||||
int secondLineStart = text.indexOf('\n');
|
||||
if (secondLineStart == -1) {
|
||||
document.insertString(offset, "\n");
|
||||
PsiDocumentManager.getInstance(project).commitDocument(document);
|
||||
CodeStyleManager.getInstance(project).reformat(textBlock);
|
||||
text = textBlock.getText();
|
||||
int indent = getIndent(text, offsetInTextBlock + 1);
|
||||
if (indent == -1) return Result.Continue;
|
||||
editor.getCaretModel().moveToOffset(offset + 1 + indent);
|
||||
}
|
||||
else {
|
||||
int indent = getIndent(text, secondLineStart + 1);
|
||||
if (indent == -1) return Result.Continue;
|
||||
String newLine = '\n' + StringUtil.repeatSymbol(' ', indent);
|
||||
document.insertString(offset, newLine);
|
||||
PsiDocumentManager.getInstance(project).commitDocument(document);
|
||||
editor.getCaretModel().moveToOffset(offset + newLine.length());
|
||||
}
|
||||
return Result.Stop;
|
||||
}
|
||||
|
||||
@Contract("null, _ -> null")
|
||||
private static PsiElement getTextBlockAt(PsiFile file, int offset) {
|
||||
if (!isJavaFile(file)) return null;
|
||||
PsiElement token = file.findElementAt(offset);
|
||||
if (token == null || token.getNode() == null || !BasicJavaAstTreeUtil.is(token.getNode(), JavaTokenType.TEXT_BLOCK_LITERAL)) return null;
|
||||
PsiElement parent = token.getParent();
|
||||
if (!BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(parent), BASIC_LITERAL_EXPRESSION)) {
|
||||
return null;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
private static boolean isJavaFile(@Nullable PsiFile file) {
|
||||
return file != null && file.getLanguage() == JavaLanguage.INSTANCE;
|
||||
}
|
||||
private static int getIndent(@NotNull String text, int start) {
|
||||
int indent = 0;
|
||||
for (int i = start; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c == '\n') {
|
||||
indent = 0;
|
||||
continue;
|
||||
}
|
||||
if (Character.isWhitespace(c)) {
|
||||
indent++;
|
||||
continue;
|
||||
}
|
||||
return indent;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright 2000-2016 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.
|
||||
*/
|
||||
package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.formatting.Indent;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.JavaDocTokenType;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.impl.source.codeStyle.SemanticEditorPosition;
|
||||
import com.intellij.psi.impl.source.codeStyle.lineIndent.JavaLikeLangLineIndentProvider;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.intellij.formatting.Indent.Type.CONTINUATION;
|
||||
import static com.intellij.psi.impl.source.codeStyle.lineIndent.JavaLikeLangLineIndentProvider.JavaLikeElement.*;
|
||||
|
||||
public class JavaLineIndentProvider extends JavaLikeLangLineIndentProvider {
|
||||
private final static HashMap<IElementType, SemanticEditorPosition.SyntaxElement> SYNTAX_MAP = new HashMap<>();
|
||||
static {
|
||||
SYNTAX_MAP.put(TokenType.WHITE_SPACE, Whitespace);
|
||||
SYNTAX_MAP.put(JavaTokenType.SEMICOLON, Semicolon);
|
||||
SYNTAX_MAP.put(JavaTokenType.LBRACE, BlockOpeningBrace);
|
||||
SYNTAX_MAP.put(JavaTokenType.RBRACE, BlockClosingBrace);
|
||||
SYNTAX_MAP.put(JavaTokenType.LBRACKET, ArrayOpeningBracket);
|
||||
SYNTAX_MAP.put(JavaTokenType.RBRACKET, ArrayClosingBracket);
|
||||
SYNTAX_MAP.put(JavaTokenType.RPARENTH, RightParenthesis);
|
||||
SYNTAX_MAP.put(JavaTokenType.LPARENTH, LeftParenthesis);
|
||||
SYNTAX_MAP.put(JavaTokenType.COLON, Colon);
|
||||
SYNTAX_MAP.put(JavaTokenType.CASE_KEYWORD, SwitchCase);
|
||||
SYNTAX_MAP.put(JavaTokenType.DEFAULT_KEYWORD, SwitchDefault);
|
||||
SYNTAX_MAP.put(JavaTokenType.IF_KEYWORD, IfKeyword);
|
||||
SYNTAX_MAP.put(JavaTokenType.WHILE_KEYWORD, IfKeyword);
|
||||
SYNTAX_MAP.put(JavaTokenType.ELSE_KEYWORD, ElseKeyword);
|
||||
SYNTAX_MAP.put(JavaTokenType.FOR_KEYWORD, ForKeyword);
|
||||
SYNTAX_MAP.put(JavaTokenType.DO_KEYWORD, DoKeyword);
|
||||
SYNTAX_MAP.put(JavaTokenType.C_STYLE_COMMENT, BlockComment);
|
||||
SYNTAX_MAP.put(JavaDocTokenType.DOC_COMMENT_START, DocBlockStart);
|
||||
SYNTAX_MAP.put(JavaDocTokenType.DOC_COMMENT_END, DocBlockEnd);
|
||||
SYNTAX_MAP.put(JavaTokenType.COMMA, Comma);
|
||||
SYNTAX_MAP.put(JavaTokenType.END_OF_LINE_COMMENT, LineComment);
|
||||
SYNTAX_MAP.put(JavaTokenType.TRY_KEYWORD, TryKeyword);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected SemanticEditorPosition.SyntaxElement mapType(@NotNull IElementType tokenType) {
|
||||
return SYNTAX_MAP.get(tokenType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuitableForLanguage(@NotNull Language language) {
|
||||
return language.isKindOf(JavaLanguage.INSTANCE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Indent getIndentInBlock(@NotNull Project project,
|
||||
@Nullable Language language,
|
||||
@NotNull SemanticEditorPosition blockStartPosition) {
|
||||
SemanticEditorPosition beforeStart = blockStartPosition.before().beforeOptional(Whitespace);
|
||||
if (beforeStart.isAt(JavaTokenType.EQ) ||
|
||||
beforeStart.isAt(JavaTokenType.RBRACKET) ||
|
||||
beforeStart.isAt(JavaTokenType.LPARENTH)
|
||||
) {
|
||||
// For arrays like int x = {<caret>0, 1, 2}
|
||||
return getDefaultIndentFromType(CONTINUATION);
|
||||
}
|
||||
else if (beforeStart.isAt(JavaTokenType.IDENTIFIER)) {
|
||||
moveBeforeExtendsImplementsAndIdentifier(beforeStart);
|
||||
if (beforeStart.isAt(JavaTokenType.CLASS_KEYWORD) && doNotIndentClassMembers(beforeStart)) {
|
||||
return Indent.getNoneIndent();
|
||||
}
|
||||
}
|
||||
return super.getIndentInBlock(project, language, blockStartPosition);
|
||||
}
|
||||
|
||||
private static void moveBeforeExtendsImplementsAndIdentifier(@NotNull SemanticEditorPosition position) {
|
||||
while (position.isAt(JavaTokenType.IDENTIFIER) || position.isAtAnyOf(Whitespace, Comma) ||
|
||||
position.isAt(JavaTokenType.EXTENDS_KEYWORD) || position.isAt(JavaTokenType.IMPLEMENTS_KEYWORD)) {
|
||||
position.moveBefore();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean doNotIndentClassMembers(@NotNull SemanticEditorPosition position) {
|
||||
Editor editor = position.getEditor();
|
||||
CommonCodeStyleSettings javaSettings = CodeStyle.getSettings(editor).getCommonSettings(JavaLanguage.INSTANCE);
|
||||
return javaSettings.DO_NOT_INDENT_TOP_LEVEL_CLASS_MEMBERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isInsideForLikeConstruction(SemanticEditorPosition position) {
|
||||
return position.isAfterOnSameLine(ForKeyword, TryKeyword);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isInArray(@NotNull Editor editor, int offset) {
|
||||
SemanticEditorPosition position = getPosition(editor, offset);
|
||||
position.moveBefore();
|
||||
if (position.isAt(JavaTokenType.LBRACE)) {
|
||||
if (position.before().beforeOptional(Whitespace).isAt(JavaTokenType.RBRACKET)) return true;
|
||||
}
|
||||
return super.isInArray(editor, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIndentProvider(@NotNull SemanticEditorPosition position, boolean ignoreLabels) {
|
||||
return !(position.afterOptionalMix(Whitespace, BlockComment).after().isAt(Colon)
|
||||
&& position.isAt(JavaTokenType.IDENTIFIER));
|
||||
}
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
// Copyright 2000-2023 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.editorActions;
|
||||
|
||||
import com.intellij.codeInsight.definition.AbstractBasicJavaDefinitionService;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
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.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.TEXT_LITERALS;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_LITERAL_EXPRESSION;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.REFERENCE_EXPRESSION_SET;
|
||||
|
||||
public class JavaQuoteHandler extends SimpleTokenSetQuoteHandler implements JavaLikeQuoteHandler, MultiCharQuoteHandler {
|
||||
private final BasicJavaTokenSet myConcatenableStrings = BasicJavaTokenSet.create(JavaTokenType.STRING_LITERAL);
|
||||
private final BasicJavaTokenSet myAppropriateElementTypeForLiteral = BasicJavaTokenSet.orSet(
|
||||
BasicJavaTokenSet.create(JavaDocTokenType.ALL_JAVADOC_TOKENS),
|
||||
JAVA_COMMENT_OR_WHITESPACE_BIT_SET, TEXT_LITERALS,
|
||||
BasicJavaTokenSet.create(JavaTokenType.SEMICOLON, JavaTokenType.COMMA, JavaTokenType.RPARENTH, JavaTokenType.RBRACKET,
|
||||
JavaTokenType.RBRACE));
|
||||
|
||||
public JavaQuoteHandler() {
|
||||
super(BasicJavaTokenSet.orSet(TEXT_LITERALS, BasicJavaTokenSet.create(JavaDocTokenType.DOC_TAG_VALUE_QUOTE)).toTokenSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpeningQuote(HighlighterIterator iterator, int offset) {
|
||||
boolean openingQuote = super.isOpeningQuote(iterator, offset);
|
||||
if (openingQuote) {
|
||||
// check escape next
|
||||
if (!iterator.atEnd()) {
|
||||
iterator.retreat();
|
||||
if (!iterator.atEnd() && StringEscapesTokenTypes.STRING_LITERAL_ESCAPES.contains(iterator.getTokenType())) {
|
||||
openingQuote = false;
|
||||
}
|
||||
iterator.advance();
|
||||
}
|
||||
}
|
||||
return openingQuote;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosingQuote(HighlighterIterator iterator, int offset) {
|
||||
if (iterator.getTokenType() == JavaTokenType.TEXT_BLOCK_LITERAL) {
|
||||
int start = iterator.getStart(), end = iterator.getEnd();
|
||||
return end - start >= 5 && offset >= end - 3;
|
||||
}
|
||||
boolean closingQuote = super.isClosingQuote(iterator, offset);
|
||||
if (closingQuote) {
|
||||
// check escape next
|
||||
if (!iterator.atEnd()) {
|
||||
iterator.advance();
|
||||
if (!iterator.atEnd() && StringEscapesTokenTypes.STRING_LITERAL_ESCAPES.contains(iterator.getTokenType())) {
|
||||
closingQuote = false;
|
||||
}
|
||||
iterator.retreat();
|
||||
}
|
||||
}
|
||||
return closingQuote;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public TokenSet getConcatenatableStringTokenTypes() {
|
||||
return myConcatenableStrings.toTokenSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStringConcatenationOperatorRepresentation() {
|
||||
return "+";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenSet getStringTokenTypes() {
|
||||
return myLiteralTokenSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppropriateElementTypeForLiteral(@NotNull IElementType tokenType) {
|
||||
return myAppropriateElementTypeForLiteral.contains(tokenType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needParenthesesAroundConcatenation(PsiElement element) {
|
||||
// example code: "some string".length() must become ("some" + " string").length()
|
||||
return element != null && element.getParent() != null && element.getParent().getParent() != null &&
|
||||
BasicJavaAstTreeUtil.is(element.getParent().getNode(), BASIC_LITERAL_EXPRESSION) &&
|
||||
BasicJavaAstTreeUtil.is(element.getParent().getParent().getNode(), REFERENCE_EXPRESSION_SET);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence getClosingQuote(@NotNull HighlighterIterator iterator, int offset) {
|
||||
return iterator.getTokenType() == JavaTokenType.TEXT_BLOCK_LITERAL && offset == iterator.getStart() + 3 ? "\"\"\"" : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNonClosedLiteral(Editor editor, HighlighterIterator iterator, int offset) {
|
||||
if (iterator.getTokenType() == JavaTokenType.TEXT_BLOCK_LITERAL) {
|
||||
Document document = editor.getDocument();
|
||||
Project project = editor.getProject();
|
||||
PsiFile file = project == null ? null : PsiDocumentManager.getInstance(project).getPsiFile(document);
|
||||
if (file == null || !testBlocksIsAvailable(file)) return false;
|
||||
String text = document.getText();
|
||||
boolean hasOpenQuotes = StringUtil.equals(text.substring(iterator.getStart(), offset + 1), "\"\"\"");
|
||||
if (hasOpenQuotes) {
|
||||
boolean hasCloseQuotes = StringUtil.contains(text.substring(offset + 1, iterator.getEnd()), "\"\"\"");
|
||||
if (!hasCloseQuotes) return true;
|
||||
// check if parser interpreted next text block start quotes as end quotes for the current one
|
||||
int nTextBlockQuotes = StringUtil.getOccurrenceCount(text.substring(iterator.getEnd()), "\"\"\"");
|
||||
return nTextBlockQuotes % 2 != 0;
|
||||
}
|
||||
}
|
||||
return super.hasNonClosedLiteral(editor, iterator, offset);
|
||||
}
|
||||
|
||||
private static boolean testBlocksIsAvailable(@NotNull PsiFile file){
|
||||
return AbstractBasicJavaDefinitionService.getJavaDefinitionService()
|
||||
.getLanguageLevel(file).isAtLeast(LanguageLevel.JDK_15);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertClosingQuote(@NotNull Editor editor, int offset, @NotNull PsiFile file, @NotNull CharSequence closingQuote) {
|
||||
editor.getDocument().insertString(offset, "\n\"\"\"");
|
||||
Project project = file.getProject();
|
||||
PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
|
||||
PsiElement token = file.findElementAt(offset);
|
||||
if (token == null) return;
|
||||
PsiElement parent = token.getParent();
|
||||
if (parent != null && BasicJavaAstTreeUtil.is(parent.getNode(), BASIC_LITERAL_EXPRESSION)) {
|
||||
CodeStyleManager.getInstance(project).reformat(parent);
|
||||
editor.getCaretModel().moveToOffset(parent.getTextRange().getEndOffset() - 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.lang.java.parser.BasicExpressionParser;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
|
||||
public final class JavaTypingTokenSets {
|
||||
private JavaTypingTokenSets() {
|
||||
}
|
||||
|
||||
static final TokenSet INVALID_INSIDE_REFERENCE = TokenSet.create(JavaTokenType.SEMICOLON, JavaTokenType.LBRACE, JavaTokenType.RBRACE);
|
||||
|
||||
public static final TokenSet UNWANTED_TOKEN_AT_QUESTION =
|
||||
TokenSet.create(JavaTokenType.C_STYLE_COMMENT, JavaTokenType.END_OF_LINE_COMMENT, JavaTokenType.CHARACTER_LITERAL,
|
||||
JavaTokenType.STRING_LITERAL, JavaTokenType.TEXT_BLOCK_LITERAL);
|
||||
|
||||
public static final TokenSet UNWANTED_TOKEN_BEFORE_QUESTION =
|
||||
TokenSet.orSet(BasicExpressionParser.ASSIGNMENT_OPS, TokenSet.create(JavaTokenType.QUEST, JavaTokenType.COLON));
|
||||
|
||||
public static final TokenSet WANTED_TOKEN_BEFORE_QUESTION =
|
||||
// Tokens that may appear before ?: in polyadic expression that may have non-boolean result
|
||||
TokenSet.orSet(
|
||||
TokenSet.create(JavaTokenType.OR, JavaTokenType.XOR, JavaTokenType.AND),
|
||||
BasicExpressionParser.SHIFT_OPS, BasicExpressionParser.ADDITIVE_OPS, BasicExpressionParser.MULTIPLICATIVE_OPS);
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.openapi.editor.actions.WordBoundaryFilter;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class JavaWordBoundaryFilter extends WordBoundaryFilter {
|
||||
@Override
|
||||
public boolean isWordBoundary(@NotNull IElementType previousTokenType, @NotNull IElementType tokenType) {
|
||||
if (previousTokenType == JavaTokenType.GT && tokenType == JavaTokenType.EQ) return false;
|
||||
return super.isWordBoundary(previousTokenType, tokenType);
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright 2000-2023 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.editorActions;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorModificationUtil;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class JavadocLineStartHandler extends EditorActionHandler.ForEachCaret {
|
||||
private static final String WHITESPACE = " \t";
|
||||
|
||||
private final EditorActionHandler myOriginalHandler;
|
||||
private final boolean myWithSelection;
|
||||
|
||||
public JavadocLineStartHandler(@NotNull EditorActionHandler originalHandler) {
|
||||
this(originalHandler, false);
|
||||
}
|
||||
|
||||
public JavadocLineStartHandler(@NotNull EditorActionHandler originalHandler,
|
||||
boolean withSelection) {
|
||||
myOriginalHandler = originalHandler;
|
||||
myWithSelection = withSelection;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(@NotNull Editor editor, @NotNull Caret caret, DataContext dataContext) {
|
||||
Project project = editor.getProject();
|
||||
if (project != null && EditorSettingsExternalizable.getInstance().isSmartHome()) {
|
||||
Document document = editor.getDocument();
|
||||
CharSequence text = document.getImmutableCharSequence();
|
||||
int lineStartOffset = document.getLineStartOffset(caret.getLogicalPosition().line);
|
||||
int nonWsStartOffset = CharArrayUtil.shiftForward(text, lineStartOffset, WHITESPACE);
|
||||
if (CharArrayUtil.regionMatches(text, nonWsStartOffset, "/**") || CharArrayUtil.regionMatches(text, nonWsStartOffset, "*")) {
|
||||
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
|
||||
PsiFile file = psiDocumentManager.getPsiFile(document);
|
||||
if (file != null && isJavaFile(file)) {
|
||||
psiDocumentManager.commitDocument(document);
|
||||
PsiElement startElement = file.findElementAt(nonWsStartOffset);
|
||||
if (startElement == null || startElement.getNode() == null) {
|
||||
return;
|
||||
}
|
||||
IElementType type = startElement.getNode().getElementType();
|
||||
if (type == JavaDocTokenType.DOC_COMMENT_START || type == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
|
||||
int targetOffset = CharArrayUtil.shiftForward(text, startElement.getTextRange().getEndOffset(), WHITESPACE);
|
||||
if (caret.getOffset() == targetOffset) targetOffset = lineStartOffset;
|
||||
int selectionStartOffset = caret.getLeadSelectionOffset();
|
||||
caret.moveToOffset(targetOffset);
|
||||
if (myWithSelection) {
|
||||
caret.setSelection(selectionStartOffset, caret.getVisualPosition(), caret.getOffset());
|
||||
}
|
||||
else {
|
||||
caret.removeSelection();
|
||||
}
|
||||
EditorModificationUtil.scrollToCaret(editor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
myOriginalHandler.execute(editor, caret, dataContext);
|
||||
}
|
||||
|
||||
private boolean isJavaFile(@Nullable PsiFile file){
|
||||
return file instanceof AbstractBasicJavaFile;
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions;
|
||||
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
|
||||
public class JavadocLineStartWithSelectionHandler extends JavadocLineStartHandler {
|
||||
public JavadocLineStartWithSelectionHandler(EditorActionHandler originalHandler) {
|
||||
super(originalHandler, true);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface ASTNodeEnterProcessor extends EnterProcessor {
|
||||
boolean doEnter(@NotNull Editor editor, @NotNull ASTNode psiElement, boolean isModified);
|
||||
|
||||
@Override
|
||||
default boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(psiElement);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
return doEnter(editor, node, isModified);
|
||||
}
|
||||
}
|
||||
|
||||
+398
@@ -0,0 +1,398 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.codeInsight.lookup.LookupManager;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.SyntaxTraverser;
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager;
|
||||
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_BIT_SET;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public abstract class AbstractBasicJavaSmartEnterProcessor extends SmartEnterProcessor {
|
||||
private static final Logger LOG = Logger.getInstance(AbstractBasicJavaSmartEnterProcessor.class);
|
||||
|
||||
private final List<Fixer> ourFixers;
|
||||
private final EnterProcessor[] ourEnterProcessors;
|
||||
private final EnterProcessor[] ourAfterCompletionEnterProcessors;
|
||||
|
||||
protected int myFirstErrorOffset = Integer.MAX_VALUE;
|
||||
protected boolean mySkipEnter;
|
||||
private static final int MAX_ATTEMPTS = 20;
|
||||
private static final Key<Long> SMART_ENTER_TIMESTAMP = Key.create("smartEnterOriginalTimestamp");
|
||||
private final EnterProcessor myBreakerEnterProcessor;
|
||||
|
||||
protected void insertBraces(@NotNull Editor editor, int offset) {
|
||||
Document document = editor.getDocument();
|
||||
document.insertString(offset, "{");
|
||||
insertCloseBrace(editor, offset + 1);
|
||||
}
|
||||
|
||||
protected void insertCloseBrace(@NotNull Editor editor, int offset) {
|
||||
Document document = editor.getDocument();
|
||||
document.insertString(offset, "}");
|
||||
}
|
||||
|
||||
protected void insertBracesWithNewLine(Editor editor, int offset) {
|
||||
Document document = editor.getDocument();
|
||||
document.insertString(offset, "{\n");
|
||||
insertCloseBrace(editor, offset + 2);
|
||||
}
|
||||
|
||||
private static class TooManyAttemptsException extends Exception {
|
||||
}
|
||||
|
||||
private final AbstractBasicJavadocFixer myJavadocFixer;
|
||||
|
||||
protected AbstractBasicJavaSmartEnterProcessor(@NotNull List<Fixer> fixers,
|
||||
EnterProcessor @NotNull [] enterProcessors,
|
||||
EnterProcessor @NotNull [] afterCompletionEnterProcessors,
|
||||
@NotNull AbstractBasicJavadocFixer thinJavadocFixer,
|
||||
@NotNull EnterProcessor breakerEnterProcessor) {
|
||||
myBreakerEnterProcessor = breakerEnterProcessor;
|
||||
ourFixers = fixers;
|
||||
ourEnterProcessors = enterProcessors;
|
||||
ourAfterCompletionEnterProcessors = afterCompletionEnterProcessors;
|
||||
myJavadocFixer = thinJavadocFixer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile psiFile) {
|
||||
return invokeProcessor(editor, psiFile, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processAfterCompletion(@NotNull Editor editor, @NotNull PsiFile psiFile) {
|
||||
return invokeProcessor(editor, psiFile, true);
|
||||
}
|
||||
|
||||
private boolean invokeProcessor(Editor editor, PsiFile psiFile, boolean afterCompletion) {
|
||||
final Document document = editor.getDocument();
|
||||
final CharSequence textForRollback = document.getImmutableCharSequence();
|
||||
try {
|
||||
editor.putUserData(SMART_ENTER_TIMESTAMP, editor.getDocument().getModificationStamp());
|
||||
myFirstErrorOffset = Integer.MAX_VALUE;
|
||||
mySkipEnter = false;
|
||||
process(editor, psiFile, 0, afterCompletion);
|
||||
}
|
||||
catch (TooManyAttemptsException e) {
|
||||
document.replaceString(0, document.getTextLength(), textForRollback);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally {
|
||||
editor.putUserData(SMART_ENTER_TIMESTAMP, null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void process(@NotNull final Editor editor, @NotNull final PsiFile file, final int attempt, boolean afterCompletion)
|
||||
throws TooManyAttemptsException {
|
||||
if (attempt > MAX_ATTEMPTS) throw new TooManyAttemptsException();
|
||||
|
||||
try {
|
||||
commit(editor);
|
||||
if (myFirstErrorOffset != Integer.MAX_VALUE) {
|
||||
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
|
||||
}
|
||||
|
||||
myFirstErrorOffset = Integer.MAX_VALUE;
|
||||
|
||||
PsiElement atCaret = getStatementAtCaret(editor, file);
|
||||
if (atCaret == null) {
|
||||
if (myJavadocFixer.process(editor, file)) {
|
||||
return;
|
||||
}
|
||||
if (!(myBreakerEnterProcessor).doEnter(editor, file, false)) {
|
||||
plainEnter(editor);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
List<ASTNode> queue = new ArrayList<>();
|
||||
ASTNode caretNode = atCaret.getNode();
|
||||
collectAllElements(caretNode, queue, true);
|
||||
queue.add(caretNode);
|
||||
|
||||
for (ASTNode astNode : queue) {
|
||||
for (Fixer fixer : ourFixers) {
|
||||
Document document = editor.getDocument();
|
||||
int offset = myFirstErrorOffset;
|
||||
long stamp = document.getModificationStamp();
|
||||
fixer.apply(editor, this, astNode);
|
||||
Project project = file.getProject();
|
||||
if (document.getModificationStamp() != stamp || offset != myFirstErrorOffset) {
|
||||
log(fixer, project);
|
||||
}
|
||||
if (LookupManager.getInstance(project).getActiveLookup() != null) {
|
||||
return;
|
||||
}
|
||||
PsiElement psi = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (isUncommited(project) || !(psi != null && psi.isValid())) {
|
||||
moveCaretInsideBracesIfAny(editor, file);
|
||||
process(editor, file, attempt + 1, afterCompletion);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doEnter(atCaret, editor, afterCompletion);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void log(@NotNull Fixer fixer, @NotNull Project project);
|
||||
|
||||
|
||||
@Override
|
||||
public void reformat(PsiElement atCaretElement) throws IncorrectOperationException {
|
||||
if (atCaretElement == null) {
|
||||
return;
|
||||
}
|
||||
ASTNode atCaret = atCaretElement.getNode();
|
||||
ASTNode parent = atCaret.getTreeParent();
|
||||
if (BasicJavaAstTreeUtil.is(parent, BASIC_FOR_STATEMENT)) {
|
||||
atCaret = parent;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(parent, BASIC_IF_STATEMENT) &&
|
||||
atCaret == BasicJavaAstTreeUtil.getElseBranch(parent)) {
|
||||
PsiFile file = atCaretElement.getContainingFile();
|
||||
Document document = file.getViewProvider().getDocument();
|
||||
if (document != null) {
|
||||
TextRange elseIfRange = atCaret.getTextRange();
|
||||
int lineStart = document.getLineStartOffset(document.getLineNumber(elseIfRange.getStartOffset()));
|
||||
CodeStyleManager.getInstance(atCaretElement.getProject()).reformatText(file, lineStart, elseIfRange.getEndOffset());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
super.reformat(atCaretElement);
|
||||
}
|
||||
|
||||
|
||||
private void doEnter(PsiElement atCaret, Editor editor, boolean afterCompletion) throws IncorrectOperationException {
|
||||
final PsiFile psiFile = atCaret.getContainingFile();
|
||||
|
||||
if (myFirstErrorOffset != Integer.MAX_VALUE) {
|
||||
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
|
||||
reformat(editor, atCaret);
|
||||
return;
|
||||
}
|
||||
|
||||
final RangeMarker rangeMarker = createRangeMarker(atCaret);
|
||||
reformat(editor, atCaret);
|
||||
commit(editor);
|
||||
|
||||
if (!mySkipEnter) {
|
||||
ASTNode atCaretNode = BasicJavaAstTreeUtil.findElementInRange(psiFile, rangeMarker.getStartOffset(), rangeMarker.getEndOffset(),
|
||||
atCaret.getNode().getElementType());
|
||||
for (EnterProcessor processor : afterCompletion ? ourAfterCompletionEnterProcessors : ourEnterProcessors) {
|
||||
if (atCaretNode == null) {
|
||||
// Can't restore element at caret after enter processor execution!
|
||||
break;
|
||||
}
|
||||
|
||||
if (processor.doEnter(editor, BasicJavaAstTreeUtil.toPsi(atCaretNode), isModified(editor))) {
|
||||
rangeMarker.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isModified(editor) && !afterCompletion) {
|
||||
plainEnter(editor);
|
||||
}
|
||||
else {
|
||||
if (myFirstErrorOffset == Integer.MAX_VALUE) {
|
||||
editor.getCaretModel().moveToOffset(rangeMarker.getEndOffset());
|
||||
}
|
||||
else {
|
||||
editor.getCaretModel().moveToOffset(myFirstErrorOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
rangeMarker.dispose();
|
||||
}
|
||||
|
||||
private static void collectAllElements(ASTNode atCaret, List<? super ASTNode> res, boolean recurse) {
|
||||
res.add(0, atCaret);
|
||||
if (doNotStepInto(atCaret)) {
|
||||
if (!recurse) return;
|
||||
recurse = false;
|
||||
}
|
||||
|
||||
final List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(atCaret);
|
||||
for (ASTNode child : children) {
|
||||
if (BasicJavaAstTreeUtil.is(atCaret, STATEMENT_SET) &&
|
||||
BasicJavaAstTreeUtil.is(child, STATEMENT_SET) &&
|
||||
!(BasicJavaAstTreeUtil.is(atCaret, BASIC_FOR_STATEMENT)
|
||||
&& child == (BasicJavaAstTreeUtil.getForInitialization(atCaret)))) {
|
||||
continue;
|
||||
}
|
||||
collectAllElements(child, res, recurse);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean doNotStepInto(ASTNode element) {
|
||||
return BasicJavaAstTreeUtil.is(element, CLASS_SET) ||
|
||||
BasicJavaAstTreeUtil.is(element, BASIC_CODE_BLOCK) ||
|
||||
BasicJavaAstTreeUtil.is(element, STATEMENT_SET) ||
|
||||
BasicJavaAstTreeUtil.is(element, BASIC_METHOD);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected PsiElement getStatementAtCaret(Editor editor, PsiFile psiFile) {
|
||||
PsiElement atCaretElement = super.getStatementAtCaret(editor, psiFile);
|
||||
if (atCaretElement == null) {
|
||||
return null;
|
||||
}
|
||||
ASTNode atCaret = atCaretElement.getNode();
|
||||
if (BasicJavaAstTreeUtil.isWhiteSpace(atCaret)) return null;
|
||||
if (BasicJavaAstTreeUtil.is(atCaret, JavaTokenType.RBRACE)) {
|
||||
atCaret = atCaret.getTreeParent();
|
||||
boolean expressionEndingWithBrace = BasicJavaAstTreeUtil.is(atCaret, BASIC_ANONYMOUS_CLASS) ||
|
||||
BasicJavaAstTreeUtil.is(atCaret, BASIC_ARRAY_INITIALIZER_EXPRESSION) ||
|
||||
BasicJavaAstTreeUtil.is(atCaret, BASIC_CODE_BLOCK) && (
|
||||
BasicJavaAstTreeUtil.is(atCaret.getTreeParent(), BASIC_LAMBDA_EXPRESSION) ||
|
||||
BasicJavaAstTreeUtil.is(atCaret.getTreeParent(), BASIC_SWITCH_EXPRESSION));
|
||||
if (!expressionEndingWithBrace) return null;
|
||||
}
|
||||
|
||||
for (ASTNode each : SyntaxTraverser.astApi().parents(atCaret).skip(1)) {
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(each);
|
||||
if (BasicJavaAstTreeUtil.is(each, MEMBER_SET) ||
|
||||
isImportStatementBase(psiElement) ||
|
||||
BasicJavaAstTreeUtil.is(each, BASIC_PACKAGE_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(each, BASIC_ANNOTATION) &&
|
||||
psiElement != null &&
|
||||
PsiTreeUtil.hasErrorElements(psiElement)) {
|
||||
return each.getPsi();
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(each, BASIC_CODE_BLOCK) ||
|
||||
BasicJavaAstTreeUtil.is(each, JAVA_COMMENT_BIT_SET)) {
|
||||
return null;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(each, STATEMENT_SET)) {
|
||||
return BasicJavaAstTreeUtil.is(each.getTreeParent(), BASIC_FOR_STATEMENT) &&
|
||||
!PsiTreeUtil.hasErrorElements(each.getPsi()) ? each.getPsi().getParent() : each.getPsi();
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(each, BASIC_CONDITIONAL_EXPRESSION) &&
|
||||
PsiUtilCore.hasErrorElementChild(each.getPsi())) {
|
||||
return each.getPsi();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract boolean isImportStatementBase(PsiElement el);
|
||||
|
||||
protected void moveCaretInsideBracesIfAny(@NotNull final Editor editor, @NotNull final PsiFile file) throws IncorrectOperationException {
|
||||
int caretOffset = editor.getCaretModel().getOffset();
|
||||
final CharSequence chars = editor.getDocument().getCharsSequence();
|
||||
|
||||
if (CharArrayUtil.regionMatches(chars, caretOffset, "{}")) {
|
||||
caretOffset += 2;
|
||||
}
|
||||
else if (CharArrayUtil.regionMatches(chars, caretOffset, "{\n}")) {
|
||||
caretOffset += 3;
|
||||
}
|
||||
|
||||
caretOffset = CharArrayUtil.shiftBackward(chars, caretOffset - 1, " \t") + 1;
|
||||
|
||||
if (CharArrayUtil.regionMatches(chars, caretOffset - "{}".length(), "{}") ||
|
||||
CharArrayUtil.regionMatches(chars, caretOffset - "{\n}".length(), "{\n}")) {
|
||||
commit(editor);
|
||||
final CommonCodeStyleSettings settings = CodeStyle.getSettings(file).getCommonSettings(JavaLanguage.INSTANCE);
|
||||
final boolean old = settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE;
|
||||
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = false;
|
||||
PsiElement leaf = file.findElementAt(caretOffset - 1);
|
||||
PsiElement elt = BasicJavaAstTreeUtil.toPsi(
|
||||
BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toNode(leaf), BASIC_CODE_BLOCK));
|
||||
if (elt == null &&
|
||||
leaf != null &&
|
||||
leaf.getParent() != null &&
|
||||
BasicJavaAstTreeUtil.is(leaf.getParent().getNode(), CLASS_SET)) {
|
||||
elt = leaf.getParent();
|
||||
}
|
||||
reformatAndMove(editor, elt, caretOffset);
|
||||
settings.KEEP_SIMPLE_BLOCKS_IN_ONE_LINE = old;
|
||||
|
||||
reformatBlockParentIfNeeded(editor, file);
|
||||
}
|
||||
}
|
||||
|
||||
protected void reformatAndMove(@NotNull Editor editor, @Nullable PsiElement elt, int caretOffset) {
|
||||
reformat(elt);
|
||||
editor.getCaretModel().moveToOffset(caretOffset - 1);
|
||||
}
|
||||
|
||||
protected void reformat(@NotNull Editor editor, @Nullable PsiElement elt) {
|
||||
reformat(elt);
|
||||
}
|
||||
|
||||
private void reformatBlockParentIfNeeded(@NotNull Editor editor, @NotNull PsiFile file) {
|
||||
commit(editor);
|
||||
ASTNode block =
|
||||
BasicJavaAstTreeUtil.findElementOfClassAtOffset(file, editor.getCaretModel().getOffset(), BASIC_CODE_BLOCK, false);
|
||||
if (block != null &&
|
||||
BasicJavaAstTreeUtil.is(block.getTreeParent(), BASIC_BLOCK_STATEMENT) &&
|
||||
BasicJavaAstTreeUtil.is(block.getTreeParent().getTreeParent(), BASIC_FOR_STATEMENT)) {
|
||||
reformat(block.getTreeParent().getTreeParent().getPsi());
|
||||
}
|
||||
}
|
||||
|
||||
public void registerUnresolvedError(int offset) {
|
||||
if (myFirstErrorOffset > offset) {
|
||||
myFirstErrorOffset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSkipEnter(boolean skipEnter) {
|
||||
mySkipEnter = skipEnter;
|
||||
}
|
||||
|
||||
protected static void plainEnter(@NotNull final Editor editor) {
|
||||
getEnterHandler().execute(editor, editor.getCaretModel().getCurrentCaret(), EditorUtil.getEditorDataContext(editor));
|
||||
}
|
||||
|
||||
protected static EditorActionHandler getEnterHandler() {
|
||||
return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE);
|
||||
}
|
||||
|
||||
protected static boolean isModified(@NotNull final Editor editor) {
|
||||
final Long timestamp = editor.getUserData(SMART_ENTER_TIMESTAMP);
|
||||
return timestamp != null && editor.getDocument().getModificationStamp() != timestamp.longValue();
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.javadoc.AbstractBasicJavadocHelper;
|
||||
import com.intellij.openapi.editor.CaretModel;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Serves as a facade for javadoc smart completion.
|
||||
* <p/>
|
||||
* Thread-safe.
|
||||
*/
|
||||
public abstract class AbstractBasicJavadocFixer {
|
||||
|
||||
private final AbstractBasicJavadocHelper myHelper;;
|
||||
|
||||
public AbstractBasicJavadocFixer(@NotNull AbstractBasicJavadocHelper helper) { myHelper = helper; }
|
||||
|
||||
/**
|
||||
* Checks if caret of the given editor is located inside javadoc and tries to perform smart completion there in case of the positive
|
||||
* answer.
|
||||
*
|
||||
* @param editor target editor
|
||||
* @param psiFile PSI file for the document exposed via the given editor
|
||||
* @return {@code true} if smart completion was performed; {@code false} otherwise
|
||||
*/
|
||||
public boolean process(@NotNull Editor editor, @NotNull PsiFile psiFile) {
|
||||
// Check parameter description completion.
|
||||
final CaretModel caretModel = editor.getCaretModel();
|
||||
final Pair<AbstractBasicJavadocHelper.JavadocParameterInfo,List<AbstractBasicJavadocHelper.JavadocParameterInfo>> pair =
|
||||
myHelper.parse(psiFile, editor, caretModel.getOffset());
|
||||
|
||||
if (pair.first == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final AbstractBasicJavadocHelper.JavadocParameterInfo next = findNext(pair.second, pair.first);
|
||||
if (next == null) {
|
||||
final int line = pair.first.lastLine + 1;
|
||||
final Document document = editor.getDocument();
|
||||
if (line < document.getLineCount()) {
|
||||
StringBuilder indent = new StringBuilder();
|
||||
boolean insertIndent = true;
|
||||
final CharSequence text = document.getCharsSequence();
|
||||
for (int i = document.getLineStartOffset(line), max = document.getLineEndOffset(line); i < max; i++) {
|
||||
final char c = text.charAt(i);
|
||||
if (c == ' ' || c == '\t') {
|
||||
indent.append(c);
|
||||
continue;
|
||||
}
|
||||
else if (c == '*') {
|
||||
indent.append("* ");
|
||||
if (i < max - 1 && text.charAt(i + 1) != '/') {
|
||||
insertIndent = false;
|
||||
}
|
||||
}
|
||||
indent.append("\n");
|
||||
break;
|
||||
}
|
||||
if (insertIndent) {
|
||||
document.insertString(document.getLineStartOffset(line), indent);
|
||||
}
|
||||
}
|
||||
moveCaretToTheLineEndIfPossible(editor, line);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (next.parameterDescriptionStartPosition != null) {
|
||||
myHelper.navigate(next.parameterDescriptionStartPosition, editor, psiFile.getProject());
|
||||
}
|
||||
else {
|
||||
final LogicalPosition position = myHelper.calculateDescriptionStartPosition(psiFile, pair.second, next);
|
||||
myHelper.navigate(position, editor, psiFile.getProject());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void moveCaretToTheLineEndIfPossible(@NotNull Editor editor, int line) {
|
||||
final Document document = editor.getDocument();
|
||||
final CaretModel caretModel = editor.getCaretModel();
|
||||
int offset;
|
||||
if (line >= document.getLineCount()) {
|
||||
offset = document.getTextLength();
|
||||
}
|
||||
else {
|
||||
offset = document.getLineEndOffset(line);
|
||||
}
|
||||
caretModel.moveToOffset(offset);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static AbstractBasicJavadocHelper.JavadocParameterInfo findNext(@NotNull Collection<? extends AbstractBasicJavadocHelper.JavadocParameterInfo> data,
|
||||
@NotNull AbstractBasicJavadocHelper.JavadocParameterInfo anchor)
|
||||
{
|
||||
boolean returnNow = false;
|
||||
for (AbstractBasicJavadocHelper.JavadocParameterInfo info : data) {
|
||||
if (returnNow) {
|
||||
return info;
|
||||
}
|
||||
returnNow = info == anchor;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.tree.TreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public abstract class AbstractBasicSemicolonFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (fixReturn(editor, psiElement)) return;
|
||||
if (fixForUpdate(editor, astNode)) return;
|
||||
fixAfterLastValidElement(editor, astNode);
|
||||
}
|
||||
|
||||
protected abstract boolean fixReturn(@NotNull Editor editor, @Nullable PsiElement astNode);
|
||||
|
||||
protected abstract boolean getSpaceAfterSemicolon(@NotNull PsiElement psiElement);
|
||||
|
||||
private boolean fixForUpdate(@NotNull Editor editor, @Nullable ASTNode astNode) {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_FOR_STATEMENT))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ASTNode condition = BasicJavaAstTreeUtil.getForCondition(astNode);
|
||||
if (BasicJavaAstTreeUtil.getForUpdate(astNode) != null || condition == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TextRange range = condition.getTextRange();
|
||||
Document document = editor.getDocument();
|
||||
CharSequence text = document.getCharsSequence();
|
||||
for (int i = range.getEndOffset() - 1, max = astNode.getTextRange().getEndOffset(); i < max; i++) {
|
||||
if (text.charAt(i) == ';') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
String toInsert = ";";
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (psiElement != null && getSpaceAfterSemicolon(psiElement)) {
|
||||
toInsert += " ";
|
||||
}
|
||||
document.insertString(range.getEndOffset(), toInsert);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void fixAfterLastValidElement(@NotNull Editor editor, @Nullable ASTNode astNode) {
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (astNode == null || psiElement == null) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_EXPRESSION_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_DECLARATION_STATEMENT) ||
|
||||
isImportStatementBase(psiElement) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_DO_WHILE_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_RETURN_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_THROW_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_BREAK_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_CONTINUE_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_YIELD_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_ASSERT_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_PACKAGE_STATEMENT) ||
|
||||
isStandaloneField(psiElement) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_METHOD) &&
|
||||
BasicJavaAstTreeUtil.getCodeBlock(astNode) == null &&
|
||||
!isMethodShouldHaveBody(psiElement) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_REQUIRES_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_OPENS_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_EXPORTS_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_USES_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_PROVIDES_STATEMENT)) {
|
||||
String text = astNode.getText();
|
||||
|
||||
int tailLength = 0;
|
||||
ASTNode leaf = TreeUtil.findLastLeaf(astNode);
|
||||
while (leaf != null && JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(leaf.getElementType())) {
|
||||
tailLength += leaf.getTextLength();
|
||||
leaf = TreeUtil.prevLeaf(leaf);
|
||||
}
|
||||
if (leaf == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tailLength > 0) {
|
||||
text = text.substring(0, text.length() - tailLength);
|
||||
}
|
||||
|
||||
int insertionOffset = leaf.getTextRange().getEndOffset();
|
||||
Document doc = editor.getDocument();
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_FIELD) &&
|
||||
(BasicJavaAstTreeUtil.hasModifierProperty(astNode, JavaTokenType.ABSTRACT_KEYWORD))) {
|
||||
// abstract rarely seem to be field. It is rather incomplete method.
|
||||
doc.insertString(insertionOffset, "()");
|
||||
insertionOffset += "()".length();
|
||||
}
|
||||
|
||||
// Like:
|
||||
// assert x instanceof Type
|
||||
// String s = "hello";
|
||||
// Here, String is parsed as name of the pattern variable, and we have an assignment, instead of declaration
|
||||
ASTNode error = astNode.getLastChildNode();
|
||||
if (BasicJavaAstTreeUtil.is(error, TokenType.ERROR_ELEMENT) &&
|
||||
BasicJavaAstTreeUtil.is(error.getTreePrev(), BASIC_INSTANCE_OF_EXPRESSION) &&
|
||||
BasicJavaAstTreeUtil.is(error.getTreePrev().getLastChildNode(), BASIC_TYPE_TEST_PATTERN)) {
|
||||
ASTNode variable = BasicJavaAstTreeUtil.getPatternVariable(error.getTreePrev().getLastChildNode());
|
||||
PsiElement skipWhitespacesForward = PsiTreeUtil.skipWhitespacesForward(psiElement);
|
||||
ASTNode assignmentExpr = BasicJavaAstTreeUtil.getExpression(BasicJavaAstTreeUtil.toNode(skipWhitespacesForward));
|
||||
if (variable != null &&
|
||||
BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(skipWhitespacesForward), BASIC_EXPRESSION_STATEMENT) &&
|
||||
BasicJavaAstTreeUtil.is(assignmentExpr, BASIC_ASSIGNMENT_EXPRESSION) &&
|
||||
JavaTokenType.EQ.equals(BasicJavaAstTreeUtil.getAssignmentOperationTokenType(assignmentExpr))) {
|
||||
ASTNode identifier = BasicJavaAstTreeUtil.getNameIdentifier(variable);
|
||||
if (identifier != null &&
|
||||
BasicJavaAstTreeUtil.toPsi(identifier.getTreePrev()) instanceof PsiWhiteSpace ws &&
|
||||
ws.getText().contains("\n") &&
|
||||
editor.getCaretModel().getOffset() < identifier.getTextRange().getStartOffset()) {
|
||||
insertionOffset = ws.getTextRange().getStartOffset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!StringUtil.endsWithChar(text, ';')) {
|
||||
ASTNode parent = astNode.getTreeParent();
|
||||
String toInsert = ";";
|
||||
if (BasicJavaAstTreeUtil.is(parent, BASIC_FOR_STATEMENT)) {
|
||||
if (BasicJavaAstTreeUtil.getForUpdate(parent) == astNode) {
|
||||
return;
|
||||
}
|
||||
if (getSpaceAfterSemicolon(psiElement)) {
|
||||
toInsert += " ";
|
||||
}
|
||||
}
|
||||
|
||||
doc.insertString(insertionOffset, toInsert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMethodShouldHaveBody(@Nullable PsiElement psiElement){
|
||||
return AfterSemicolonEnterProcessor.shouldHaveBody(BasicJavaAstTreeUtil.toNode(psiElement));
|
||||
}
|
||||
|
||||
protected abstract boolean isImportStatementBase(@Nullable PsiElement psiElement);
|
||||
|
||||
private static boolean isStandaloneField(@Nullable PsiElement psiElement) {
|
||||
if (psiElement == null || !BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(psiElement), BASIC_FIELD)) return false;
|
||||
PsiElement node = PsiTreeUtil.nextLeaf(psiElement, true);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
return !",".equals(node.getText());
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiErrorElement;
|
||||
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaElementType;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class AfterSemicolonEnterProcessor implements ASTNodeEnterProcessor {
|
||||
|
||||
@Override
|
||||
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (psiElement == null) {
|
||||
return false;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_EXPRESSION_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_DECLARATION_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_DO_WHILE_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_RETURN_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_THROW_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_BREAK_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_CONTINUE_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_YIELD_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_ASSERT_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, Set.of(BasicJavaElementType.BASIC_FIELD, BasicJavaElementType.BASIC_ENUM_CONSTANT)) ||
|
||||
isImportStatementBase(psiElement) ||
|
||||
isMethodWithoutBody(psiElement)) {
|
||||
int errorOffset = getErrorElementOffset(psiElement);
|
||||
int elementEndOffset = astNode.getTextRange().getEndOffset();
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BasicJavaElementType.BASIC_ENUM_CONSTANT)) {
|
||||
final CharSequence text = editor.getDocument().getCharsSequence();
|
||||
final int commaOffset = CharArrayUtil.shiftForwardUntil(text, elementEndOffset, ",");
|
||||
if (commaOffset < text.length()) {
|
||||
elementEndOffset = commaOffset + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorOffset >= 0 && errorOffset < elementEndOffset) {
|
||||
final CharSequence text = editor.getDocument().getCharsSequence();
|
||||
if (text.charAt(errorOffset) == ' ' && text.charAt(errorOffset + 1) == ';') {
|
||||
errorOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
editor.getCaretModel().moveToOffset(errorOffset >= 0 ? errorOffset : elementEndOffset);
|
||||
return isModified;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean shouldHaveBody(@Nullable ASTNode element) {
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
ASTNode containingClass = BasicJavaAstTreeUtil.getParentOfType(element, CLASS_SET);
|
||||
if (containingClass == null) return false;
|
||||
if (BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.ABSTRACT_KEYWORD) ||
|
||||
BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.NATIVE_KEYWORD)) {
|
||||
return false;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.PRIVATE_KEYWORD)) return true;
|
||||
if (BasicJavaAstTreeUtil.isInterfaceEnumClassOrRecord(containingClass, JavaTokenType.INTERFACE_KEYWORD) &&
|
||||
!BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.DEFAULT_KEYWORD) &&
|
||||
!BasicJavaAstTreeUtil.hasModifierProperty(element, JavaTokenType.STATIC_KEYWORD)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isMethodWithoutBody(@Nullable PsiElement psiElement){
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(psiElement);
|
||||
return BasicJavaAstTreeUtil.is(node, BASIC_METHOD) &&
|
||||
!shouldHaveBody(node);
|
||||
}
|
||||
|
||||
private boolean isImportStatementBase(@Nullable PsiElement psiElement){
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(psiElement);
|
||||
return
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_IMPORT_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_IMPORT_STATIC_STATEMENT);
|
||||
}
|
||||
|
||||
private static int getErrorElementOffset(PsiElement elt) {
|
||||
final int[] offset = {-1};
|
||||
elt.accept(new PsiRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitErrorElement(@NotNull PsiErrorElement element) {
|
||||
if (offset[0] == -1) offset[0] = element.getTextRange().getStartOffset();
|
||||
}
|
||||
});
|
||||
return offset[0];
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CODE_BLOCK;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.STATEMENT_SET;
|
||||
|
||||
public class BlockBraceFixer implements Fixer{
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (psiElement == null) {
|
||||
return;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_CODE_BLOCK) && afterUnmatchedBrace(editor, psiElement.getContainingFile().getFileType())) {
|
||||
int stopOffset = astNode.getTextRange().getEndOffset();
|
||||
List<ASTNode> statements = BasicJavaAstTreeUtil.getChildren(astNode).stream().filter(node->
|
||||
BasicJavaAstTreeUtil.is(node, STATEMENT_SET)
|
||||
).toList();
|
||||
if (!statements.isEmpty()) {
|
||||
stopOffset = statements.get(0).getTextRange().getEndOffset();
|
||||
}
|
||||
editor.getDocument().insertString(stopOffset, "}");
|
||||
}
|
||||
}
|
||||
|
||||
static boolean afterUnmatchedBrace(Editor editor, FileType fileType) {
|
||||
return EnterAfterUnmatchedBraceHandler.isAfterUnmatchedLBrace(editor, editor.getCaretModel().getOffset(), fileType);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CATCH_SECTION;
|
||||
|
||||
public class CatchDeclarationFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_CATCH_SECTION)) {
|
||||
final Document doc = editor.getDocument();
|
||||
|
||||
final int catchStart = astNode.getTextRange().getStartOffset();
|
||||
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(catchStart));
|
||||
|
||||
final ASTNode catchBlock = BasicJavaAstTreeUtil.getCatchBlock(astNode);
|
||||
if (catchBlock != null) {
|
||||
stopOffset = Math.min(stopOffset, catchBlock.getTextRange().getStartOffset());
|
||||
}
|
||||
stopOffset = Math.min(stopOffset, astNode.getTextRange().getEndOffset());
|
||||
|
||||
final ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
|
||||
if (lParenth == null) {
|
||||
doc.replaceString(catchStart, stopOffset, "catch ()");
|
||||
processor.registerUnresolvedError(catchStart + "catch (".length());
|
||||
}
|
||||
else {
|
||||
if (BasicJavaAstTreeUtil.getParameter(astNode) == null) {
|
||||
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.getRParenth(astNode) == null) {
|
||||
doc.insertString(stopOffset, ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.
|
||||
*/
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorModificationUtilEx;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicElementTypes;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.DOC_COMMENT;
|
||||
|
||||
public class CommentBreakerEnterProcessor implements ASTNodeEnterProcessor {
|
||||
|
||||
private final BasicJavaTokenSet myCommentTypes = BasicJavaTokenSet.orSet(
|
||||
BasicElementTypes.JAVA_PLAIN_COMMENT_BIT_SET, BasicJavaTokenSet.create(DOC_COMMENT)
|
||||
);
|
||||
|
||||
@Override
|
||||
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (isModified || psiElement == null) return false;
|
||||
final PsiElement atCaret = psiElement.getContainingFile().findElementAt(editor.getCaretModel().getOffset());
|
||||
if (atCaret == null) return false;
|
||||
final ASTNode comment = BasicJavaAstTreeUtil.getParentOfType(atCaret.getNode(), myCommentTypes, false);
|
||||
if (comment != null) {
|
||||
plainEnter(editor);
|
||||
if (BasicJavaAstTreeUtil.is(comment, JavaTokenType.END_OF_LINE_COMMENT)) {
|
||||
EditorModificationUtilEx.insertStringAtCaret(editor, "// ");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void plainEnter(Editor editor) {
|
||||
getEnterHandler().execute(editor, editor.getCaretModel().getCurrentCaret(), EditorUtil.getEditorDataContext(editor));
|
||||
}
|
||||
|
||||
private static EditorActionHandler getEnterHandler() {
|
||||
return EditorActionManager.getInstance().getActionHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE);
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_BLOCK_STATEMENT;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_DO_WHILE_STATEMENT;
|
||||
|
||||
public class DoWhileConditionFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_DO_WHILE_STATEMENT)) {
|
||||
final Document doc = editor.getDocument();
|
||||
ASTNode whileKeyword = BasicJavaAstTreeUtil.getWhileKeyword(astNode);
|
||||
ASTNode doWhileBody = BasicJavaAstTreeUtil.getDoWhileBody(astNode);
|
||||
if (doWhileBody == null || !(BasicJavaAstTreeUtil.is(doWhileBody, BASIC_BLOCK_STATEMENT)) && whileKeyword == null) {
|
||||
final int startOffset = astNode.getTextRange().getStartOffset();
|
||||
doc.replaceString(startOffset, startOffset + "do".length(), "do {} while()");
|
||||
return;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.getWhileCondition(astNode) == null) {
|
||||
if (whileKeyword == null) {
|
||||
final int endOffset = astNode.getTextRange().getEndOffset();
|
||||
doc.insertString(endOffset, "while()");
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.getLParenth(astNode) == null || BasicJavaAstTreeUtil.getRParenth(astNode) == null) {
|
||||
final TextRange whileRange = whileKeyword.getTextRange();
|
||||
doc.replaceString(whileRange.getStartOffset(), whileRange.getEndOffset(), "while()");
|
||||
}
|
||||
else {
|
||||
ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
|
||||
if (lParenth != null) {
|
||||
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.
|
||||
*/
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
|
||||
public interface EnterProcessor {
|
||||
boolean doEnter(Editor editor, PsiElement psiElement, boolean isModified);
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.
|
||||
*/
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ENUM_CONSTANT;
|
||||
|
||||
public class EnumFieldFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_ENUM_CONSTANT)) {
|
||||
int insertionOffset = astNode.getTextRange().getEndOffset();
|
||||
Document doc = editor.getDocument();
|
||||
final CharSequence text = doc.getCharsSequence();
|
||||
final int probableCommaOffset = CharArrayUtil.shiftForward(text, insertionOffset, " \t");
|
||||
if (probableCommaOffset >= text.length() || text.charAt(probableCommaOffset) != ',') {
|
||||
doc.insertString(insertionOffset, ",");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// Copyright 2000-2020 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.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface Fixer {
|
||||
void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException;
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.BaseJavaJspElementType;
|
||||
import com.intellij.application.options.CodeStyle;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_EMPTY_STATEMENT;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_FOR_STATEMENT;
|
||||
|
||||
/**
|
||||
* {@link Fixer} that handles use-cases like below:
|
||||
* <b>before:</b>
|
||||
* <pre>
|
||||
* void foo() {
|
||||
* for[caret]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <b>after:</b>
|
||||
* <pre>
|
||||
* void foo() {
|
||||
* for ([caret]) {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class ForStatementFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_FOR_STATEMENT))) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
|
||||
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
|
||||
if (lParenth == null || rParenth == null) {
|
||||
final TextRange textRange = astNode.getTextRange();
|
||||
editor.getDocument().replaceString(textRange.getStartOffset(), textRange.getEndOffset(), "for () {\n}");
|
||||
processor.registerUnresolvedError(textRange.getStartOffset() + "for (".length());
|
||||
return;
|
||||
}
|
||||
|
||||
final ASTNode initialization = BasicJavaAstTreeUtil.getForInitialization(astNode);
|
||||
if (initialization == null) {
|
||||
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
|
||||
return;
|
||||
}
|
||||
|
||||
final ASTNode condition = BasicJavaAstTreeUtil.getForCondition(astNode);
|
||||
if (condition == null) {
|
||||
boolean endlessLoop = BasicJavaAstTreeUtil.is(initialization, BASIC_EMPTY_STATEMENT) &&
|
||||
BasicJavaAstTreeUtil.getForUpdate(astNode) == null;
|
||||
if (!endlessLoop) {
|
||||
registerErrorOffset(editor, processor, initialization, astNode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.getForUpdate(astNode) == null) {
|
||||
registerErrorOffset(editor, processor, condition, astNode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link JavaSmartEnterProcessor#registerUnresolvedError(int) registers target offset} taking care of the situation when
|
||||
* current code style implies white space after 'for' part's semicolon.
|
||||
*
|
||||
* @param editor target editor
|
||||
* @param processor target smart enter processor
|
||||
* @param lastValidForPart last valid element of the target 'for' loop
|
||||
* @param forStatement PSI element for the target 'for' loop
|
||||
*/
|
||||
private void registerErrorOffset(@NotNull Editor editor, @NotNull AbstractBasicJavaSmartEnterProcessor processor,
|
||||
@NotNull ASTNode lastValidForPart, @NotNull ASTNode forStatement) {
|
||||
final Project project = editor.getProject();
|
||||
int offset = lastValidForPart.getTextRange().getEndOffset();
|
||||
if (project != null && CodeStyle.getSettings(editor).getCommonSettings(JavaLanguage.INSTANCE).SPACE_AFTER_COMMA) {
|
||||
if (editor.getDocument().getCharsSequence().charAt(lastValidForPart.getTextRange().getEndOffset() - 1) != ';') {
|
||||
offset++;
|
||||
}
|
||||
for (ASTNode element = lastValidForPart.getTreeNext();
|
||||
element != null && element != BasicJavaAstTreeUtil.getRParenth(forStatement) && element.getTreeParent() == forStatement;
|
||||
element = element.getTreeNext()) {
|
||||
if (isWhiteSpaceIncludingJsp(element) && element.getTextLength() > 0) {
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processor.registerUnresolvedError(offset);
|
||||
}
|
||||
|
||||
private static boolean isWhiteSpaceIncludingJsp(@NotNull ASTNode node) {
|
||||
return BaseJavaJspElementType.WHITE_SPACE_BIT_SET.contains(node.getElementType());
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiKeyword;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilCore;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_PLAIN_COMMENT_BIT_SET;
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.DOC_COMMENT;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class IfConditionFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_IF_STATEMENT)) {
|
||||
final Document doc = editor.getDocument();
|
||||
final ASTNode rParen = BasicJavaAstTreeUtil.getRParenth(astNode);
|
||||
final ASTNode lParen = BasicJavaAstTreeUtil.getLParenth(astNode);
|
||||
final ASTNode condition = BasicJavaAstTreeUtil.getIfCondition(astNode);
|
||||
|
||||
if (condition == null) {
|
||||
if (lParen == null || rParen == null) {
|
||||
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(astNode.getTextRange().getStartOffset()));
|
||||
final ASTNode then = BasicJavaAstTreeUtil.getThenBranch(astNode);
|
||||
if (then != null) {
|
||||
stopOffset = Math.min(stopOffset, then.getTextRange().getStartOffset());
|
||||
}
|
||||
stopOffset = Math.min(stopOffset, astNode.getTextRange().getEndOffset());
|
||||
|
||||
ASTNode lastChild = astNode.getLastChildNode();
|
||||
String innerComment = "";
|
||||
String lastComment = "";
|
||||
if (lParen != null && PsiUtilCore.getElementType(lastChild) == JavaTokenType.C_STYLE_COMMENT) {
|
||||
innerComment = lastChild.getText();
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(lastChild, DOC_COMMENT) ||
|
||||
BasicJavaAstTreeUtil.is(lastChild, JAVA_PLAIN_COMMENT_BIT_SET)
|
||||
) {
|
||||
lastComment = lastChild.getText();
|
||||
}
|
||||
|
||||
String prefix = "if (" + innerComment;
|
||||
doc.replaceString(astNode.getTextRange().getStartOffset(), stopOffset, prefix + ")" + lastComment);
|
||||
|
||||
processor.registerUnresolvedError(astNode.getTextRange().getStartOffset() + prefix.length());
|
||||
}
|
||||
else {
|
||||
processor.registerUnresolvedError(lParen.getTextRange().getEndOffset());
|
||||
}
|
||||
}
|
||||
else if (rParen == null) {
|
||||
doc.insertString(condition.getTextRange().getEndOffset(), ")");
|
||||
}
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(astNode, EXPRESSION_SET) &&
|
||||
BasicJavaAstTreeUtil.is(astNode.getTreeParent(), BASIC_EXPRESSION_STATEMENT)) {
|
||||
PsiElement psi = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (psi != null) {
|
||||
PsiElement prevLeaf = PsiTreeUtil.prevVisibleLeaf(psi);
|
||||
if (prevLeaf != null && prevLeaf.textMatches(PsiKeyword.IF)) {
|
||||
Document doc = editor.getDocument();
|
||||
doc.insertString(astNode.getTextRange().getEndOffset(), ")");
|
||||
doc.insertString(astNode.getTextRange().getStartOffset(), "(");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class LeaveCodeBlockEnterProcessor implements ASTNodeEnterProcessor {
|
||||
private final BasicJavaTokenSet CONTROL_FLOW_ELEMENT_TYPES =
|
||||
BasicJavaTokenSet.create(BASIC_IF_STATEMENT, BASIC_WHILE_STATEMENT, BASIC_DO_WHILE_STATEMENT, BASIC_FOR_STATEMENT,
|
||||
BASIC_FOREACH_STATEMENT);
|
||||
|
||||
|
||||
@Override
|
||||
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
|
||||
ASTNode parent = astNode.getTreeParent();
|
||||
if (!(BasicJavaAstTreeUtil.is(parent, BASIC_CODE_BLOCK))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CONTROL_FLOW_ELEMENT_TYPES.contains(astNode.getElementType())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean leaveCodeBlock = isControlFlowBreak(astNode);
|
||||
if (!leaveCodeBlock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int offset = parent.getTextRange().getEndOffset();
|
||||
|
||||
// Check if there is empty line after the code block. Just move caret there in the case of the positive answer.
|
||||
final CharSequence text = editor.getDocument().getCharsSequence();
|
||||
if (offset < text.length() - 1) {
|
||||
final int i = CharArrayUtil.shiftForward(text, offset + 1, " \t");
|
||||
if (i < text.length() && text.charAt(i) == '\n') {
|
||||
editor.getCaretModel().moveToOffset(offset + 1);
|
||||
EditorActionManager actionManager = EditorActionManager.getInstance();
|
||||
EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_MOVE_LINE_END);
|
||||
final DataContext dataContext = DataManager.getInstance().getDataContext(editor.getComponent());
|
||||
actionHandler.execute(editor, editor.getCaretModel().getCurrentCaret(), dataContext);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
editor.getCaretModel().moveToOffset(offset);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles situations like the one below:
|
||||
* <pre>
|
||||
* void foo(int i) {
|
||||
* if (i < 0) {
|
||||
* return;[caret]
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <b>Output:</b>
|
||||
* <pre>
|
||||
* void foo(int i) {
|
||||
* if (i < 0) {
|
||||
* return;
|
||||
* }
|
||||
* [caret]
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
private static boolean isControlFlowBreak(@Nullable ASTNode element) {
|
||||
return BasicJavaAstTreeUtil.is(element, BASIC_RETURN_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(element, BASIC_THROW_STATEMENT);
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LiteralFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode)
|
||||
throws IncorrectOperationException {
|
||||
if (astNode.getElementType() == JavaTokenType.STRING_LITERAL &&
|
||||
!StringUtil.endsWithChar(astNode.getText(), '\"')) {
|
||||
editor.getDocument().insertString(astNode.getTextRange().getEndOffset(), "\"");
|
||||
}
|
||||
else if (astNode.getElementType() == JavaTokenType.CHARACTER_LITERAL &&
|
||||
!StringUtil.endsWithChar(astNode.getText(), '\'')) {
|
||||
editor.getDocument().insertString(astNode.getTextRange().getEndOffset(), "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_NEW_EXPRESSION;
|
||||
|
||||
public class MissingArrayConstructorBracketFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_NEW_EXPRESSION))) return;
|
||||
int count = 0;
|
||||
for (ASTNode element = astNode.getFirstChildNode(); element != null; element = element.getTreeNext()) {
|
||||
if (BasicJavaAstTreeUtil.is(element, JavaTokenType.LBRACKET)) {
|
||||
count++;
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(element, JavaTokenType.RBRACKET)) {
|
||||
count--;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
editor.getDocument().insertString(astNode.getTextRange().getEndOffset(), "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.enter.EnterAfterUnmatchedBraceHandler;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ANNOTATION_ARRAY_INITIALIZER;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ARRAY_INITIALIZER_EXPRESSION;
|
||||
|
||||
public class MissingArrayInitializerBraceFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_ARRAY_INITIALIZER_EXPRESSION) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_ANNOTATION_ARRAY_INITIALIZER))) {
|
||||
return;
|
||||
}
|
||||
ASTNode child = astNode.getFirstChildNode();
|
||||
if (!child.getElementType().equals(JavaTokenType.LBRACE)) return;
|
||||
PsiElement psi = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (psi == null) {
|
||||
return;
|
||||
}
|
||||
if (!EnterAfterUnmatchedBraceHandler.isAfterUnmatchedLBrace(editor, child.getTextRange().getEndOffset(),
|
||||
psi.getContainingFile().getFileType())) {
|
||||
return;
|
||||
}
|
||||
ASTNode anchor = BasicJavaAstTreeUtil.findChildByType(astNode, TokenType.ERROR_ELEMENT);
|
||||
if (anchor == null) {
|
||||
PsiElement last = PsiTreeUtil.getDeepestVisibleLast(psi);
|
||||
while (last != null && last.getNode().getElementType().equals(JavaTokenType.RBRACE)) {
|
||||
last = PsiTreeUtil.prevCodeLeaf(last);
|
||||
}
|
||||
if (last != null && PsiTreeUtil.isAncestor(psi, last, true)) {
|
||||
anchor = last.getNode();
|
||||
}
|
||||
}
|
||||
int endOffset = (anchor != null ? anchor : astNode).getTextRange().getEndOffset();
|
||||
editor.getDocument().insertString(endOffset, "}");
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CATCH_SECTION;
|
||||
|
||||
public class MissingCatchBodyFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_CATCH_SECTION))) return;
|
||||
|
||||
ASTNode body = BasicJavaAstTreeUtil.getCatchBlock(astNode);
|
||||
if (body != null && BasicJavaAstTreeUtil.getLBrace(body) != null && BasicJavaAstTreeUtil.getRBrace(body) != null) return;
|
||||
|
||||
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
|
||||
if (rParenth == null) return;
|
||||
|
||||
processor.insertBraces(editor, rParenth.getTextRange().getEndOffset());
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class MissingClassBodyFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_RECORD_COMPONENT)) {
|
||||
astNode = BasicJavaAstTreeUtil.getRecordComponentContainingClass(astNode);
|
||||
}
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, CLASS_SET)) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_TYPE_PARAMETER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.getLBrace(astNode) == null && astNode != null) {
|
||||
ASTNode lastChild = astNode.getLastChildNode();
|
||||
int offset = astNode.getTextRange().getEndOffset();
|
||||
if (BasicJavaAstTreeUtil.is(lastChild, TokenType.ERROR_ELEMENT)) {
|
||||
ASTNode previous = lastChild.getTreePrev();
|
||||
if (BasicJavaAstTreeUtil.isWhiteSpace(previous)) {
|
||||
offset = previous.getTextRange().getStartOffset();
|
||||
}
|
||||
else {
|
||||
offset = lastChild.getTextRange().getStartOffset();
|
||||
}
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.isInterfaceEnumClassOrRecord(astNode, JavaTokenType.RECORD_KEYWORD) &&
|
||||
BasicJavaAstTreeUtil.getRecordHeader(astNode) == null) {
|
||||
editor.getDocument().insertString(offset, "() {}");
|
||||
editor.getCaretModel().moveToOffset(offset + 1);
|
||||
processor.setSkipEnter(true);
|
||||
}
|
||||
else {
|
||||
processor.insertBracesWithNewLine(editor, offset);
|
||||
editor.getCaretModel().moveToOffset(offset + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiKeyword;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_BLOCK_STATEMENT;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_IF_STATEMENT;
|
||||
|
||||
public class MissingIfBranchesFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_IF_STATEMENT))) return;
|
||||
|
||||
final Document doc = editor.getDocument();
|
||||
final ASTNode elseElement = BasicJavaAstTreeUtil.getElseElement(astNode);
|
||||
if (elseElement != null) {
|
||||
handleBranch(doc, astNode, elseElement, BasicJavaAstTreeUtil.getElseBranch(astNode));
|
||||
}
|
||||
|
||||
ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
|
||||
assert rParenth != null;
|
||||
handleBranch(doc, astNode, rParenth, BasicJavaAstTreeUtil.getThenBranch(astNode));
|
||||
}
|
||||
|
||||
private static void handleBranch(@NotNull Document doc,
|
||||
@NotNull ASTNode ifStatement,
|
||||
@NotNull ASTNode beforeBranch,
|
||||
@Nullable ASTNode branch) {
|
||||
if (BasicJavaAstTreeUtil.is(branch, BASIC_BLOCK_STATEMENT) ||
|
||||
PsiKeyword.ELSE.equals(beforeBranch.getText()) && BasicJavaAstTreeUtil.is(branch, BASIC_IF_STATEMENT)) {
|
||||
return;
|
||||
}
|
||||
boolean transformingOneLiner = branch != null && (startLine(doc, beforeBranch) == startLine(doc, branch) ||
|
||||
startCol(doc, ifStatement) < startCol(doc, branch));
|
||||
|
||||
if (!transformingOneLiner) {
|
||||
doc.insertString(beforeBranch.getTextRange().getEndOffset(), "{}");
|
||||
}
|
||||
else {
|
||||
doc.insertString(beforeBranch.getTextRange().getEndOffset(), "{");
|
||||
doc.insertString(branch.getTextRange().getEndOffset() + 1, "}");
|
||||
}
|
||||
}
|
||||
|
||||
private static int startLine(Document doc, @NotNull ASTNode astNode) {
|
||||
return doc.getLineNumber(astNode.getTextRange().getStartOffset());
|
||||
}
|
||||
|
||||
private static int startCol(Document doc, @NotNull ASTNode astNode) {
|
||||
int offset = astNode.getTextRange().getStartOffset();
|
||||
return offset - doc.getLineStartOffset(doc.getLineNumber(offset));
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.
|
||||
*/
|
||||
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class MissingLambdaBodyFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
ASTNode body = null;
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_LAMBDA_EXPRESSION)) {
|
||||
final ASTNode lastChild = astNode.getLastChildNode();
|
||||
if (BasicJavaAstTreeUtil.is(lastChild, EXPRESSION_SET) ||
|
||||
BasicJavaAstTreeUtil.is(lastChild, BASIC_CODE_BLOCK)) {
|
||||
body = lastChild;
|
||||
}
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_LABELED_RULE)) {
|
||||
body = BasicJavaAstTreeUtil.getRuleBody(astNode);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
if (body != null) return;
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
if (psiElement == null) {
|
||||
return;
|
||||
}
|
||||
PsiElement arrow = PsiTreeUtil.getDeepestVisibleLast(psiElement);
|
||||
if (arrow == null || !arrow.getNode().getElementType().equals(JavaTokenType.ARROW)) return;
|
||||
int offset = arrow.getTextRange().getEndOffset();
|
||||
processor.insertBracesWithNewLine(editor, offset);
|
||||
editor.getCaretModel().moveToOffset(offset + 1);
|
||||
processor.commit(editor);
|
||||
processor.reformat(editor, psiElement);
|
||||
processor.setSkipEnter(BasicJavaAstTreeUtil.is(astNode, BASIC_LAMBDA_EXPRESSION));
|
||||
}
|
||||
}
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2000-2009 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.
|
||||
*/
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class MissingLoopBodyFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
ASTNode loopStatement = getLoopParent(astNode);
|
||||
if (loopStatement == null) return;
|
||||
|
||||
final Document doc = editor.getDocument();
|
||||
ASTNode body;
|
||||
if (BasicJavaAstTreeUtil.is(loopStatement, BASIC_FOR_STATEMENT)) {
|
||||
body = BasicJavaAstTreeUtil.getForBody(loopStatement);
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(loopStatement, BASIC_FOREACH_STATEMENT)) {
|
||||
body = BasicJavaAstTreeUtil.getForeachBody(loopStatement);
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(loopStatement, BASIC_WHILE_STATEMENT)) {
|
||||
body = BasicJavaAstTreeUtil.getWhileBody(loopStatement);
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(body, BASIC_BLOCK_STATEMENT)) return;
|
||||
if (body != null && startLine(doc, body) == startLine(doc, loopStatement)) return;
|
||||
|
||||
ASTNode eltToInsertAfter = BasicJavaAstTreeUtil.getRParenth(loopStatement);
|
||||
fixLoopBody(editor, processor, loopStatement, doc, body, eltToInsertAfter);
|
||||
}
|
||||
|
||||
private static ASTNode getLoopParent(@NotNull ASTNode element) {
|
||||
ASTNode statement = BasicJavaAstTreeUtil.getParentOfType(element, BasicJavaTokenSet.create(BASIC_FOREACH_STATEMENT,
|
||||
BASIC_FOR_STATEMENT,
|
||||
BASIC_WHILE_STATEMENT));
|
||||
if (statement == null) return null;
|
||||
if (BasicJavaAstTreeUtil.is(statement, BASIC_FOREACH_STATEMENT)) {
|
||||
return isForEachApplicable(statement, element) ? statement : null;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(statement, BASIC_FOR_STATEMENT)) {
|
||||
return isForApplicable(statement, element) ? statement : null;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(statement, BASIC_WHILE_STATEMENT)) {
|
||||
return statement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isForApplicable(ASTNode statement, ASTNode astNode) {
|
||||
ASTNode init = BasicJavaAstTreeUtil.getForInitialization(statement);
|
||||
ASTNode update = BasicJavaAstTreeUtil.getForUpdate(statement);
|
||||
ASTNode check = BasicJavaAstTreeUtil.getForCondition(statement);
|
||||
|
||||
return isValidChild(init, astNode) || isValidChild(update, astNode) || isValidChild(check, astNode);
|
||||
}
|
||||
|
||||
private static boolean isValidChild(ASTNode ancestorNode, ASTNode node) {
|
||||
PsiElement ancestor = BasicJavaAstTreeUtil.toPsi(ancestorNode);
|
||||
PsiElement element = BasicJavaAstTreeUtil.toPsi(node);
|
||||
if (ancestor != null && element != null) {
|
||||
if (PsiTreeUtil.isAncestor(ancestor, element, false)) {
|
||||
if (PsiTreeUtil.hasErrorElements(ancestor)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isForEachApplicable(ASTNode statement, ASTNode astNode) {
|
||||
ASTNode iterated = BasicJavaAstTreeUtil.getForEachIteratedValue(statement);
|
||||
ASTNode parameter = BasicJavaAstTreeUtil.getForEachIterationParameter(statement);
|
||||
PsiElement iteratedElement = BasicJavaAstTreeUtil.toPsi(iterated);
|
||||
PsiElement parameterElement = BasicJavaAstTreeUtil.toPsi(parameter);
|
||||
PsiElement element = BasicJavaAstTreeUtil.toPsi(astNode);
|
||||
return element != null &&
|
||||
(PsiTreeUtil.isAncestor(iteratedElement, element, false) ||
|
||||
PsiTreeUtil.isAncestor(parameterElement, element, false));
|
||||
}
|
||||
|
||||
private static int startLine(Document doc, ASTNode astNode) {
|
||||
return doc.getLineNumber(astNode.getTextRange().getStartOffset());
|
||||
}
|
||||
|
||||
private static void fixLoopBody(@NotNull Editor editor,
|
||||
@NotNull AbstractBasicJavaSmartEnterProcessor processor,
|
||||
@NotNull ASTNode loop,
|
||||
@NotNull Document doc,
|
||||
@Nullable ASTNode body,
|
||||
@Nullable ASTNode eltToInsertAfter) {
|
||||
PsiElement loopElement = BasicJavaAstTreeUtil.toPsi(loop);
|
||||
if (body != null && eltToInsertAfter != null) {
|
||||
PsiElement bodyElement = BasicJavaAstTreeUtil.toPsi(body);
|
||||
if (loopElement != null && bodyElement != null && bodyIsIndented(loopElement, bodyElement)) {
|
||||
int endOffset = body.getTextRange().getEndOffset();
|
||||
doc.insertString(endOffset, "\n");
|
||||
processor.insertCloseBrace(editor, endOffset + 1);
|
||||
int offset = eltToInsertAfter.getTextRange().getEndOffset();
|
||||
doc.insertString(offset, "{");
|
||||
editor.getCaretModel().moveToOffset(endOffset + "{".length());
|
||||
processor.setSkipEnter(true);
|
||||
processor.reformat(loopElement);
|
||||
return;
|
||||
}
|
||||
}
|
||||
boolean needToClose = false;
|
||||
if (eltToInsertAfter == null) {
|
||||
eltToInsertAfter = loop;
|
||||
needToClose = true;
|
||||
}
|
||||
int offset = eltToInsertAfter.getTextRange().getEndOffset();
|
||||
if (needToClose) {
|
||||
doc.insertString(offset, ")");
|
||||
offset++;
|
||||
}
|
||||
processor.insertBraces(editor, offset);
|
||||
editor.getCaretModel().moveToOffset(offset);
|
||||
}
|
||||
|
||||
private static boolean bodyIsIndented(@NotNull PsiElement loop, @NotNull PsiElement body) {
|
||||
PsiWhiteSpace beforeBody = ObjectUtils.tryCast(body.getPrevSibling(), PsiWhiteSpace.class);
|
||||
if (beforeBody == null) return false;
|
||||
PsiWhiteSpace beforeLoop = ObjectUtils.tryCast(loop.getPrevSibling(), PsiWhiteSpace.class);
|
||||
if (beforeLoop == null) return false;
|
||||
String beforeBodyText = beforeBody.getText();
|
||||
String beforeLoopText = beforeLoop.getText();
|
||||
int beforeBodyLineBreak = beforeBodyText.lastIndexOf('\n');
|
||||
if (beforeBodyLineBreak == -1) return false;
|
||||
int beforeLoopLineBreak = beforeLoopText.lastIndexOf('\n');
|
||||
if (beforeLoopLineBreak == -1) return false;
|
||||
return beforeBodyText.length() - beforeBodyLineBreak > beforeLoopText.length() - beforeLoopLineBreak;
|
||||
}
|
||||
}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.core.JavaPsiBundle;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiErrorElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class MissingMethodBodyFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_FIELD)) {
|
||||
// replace something like `void x` with `void x() {...}`
|
||||
// while it's ambiguous whether user wants a field or a method, declaring a field is easier (just append a semicolon),
|
||||
// so completing a method looks more useful
|
||||
if (BasicJavaAstTreeUtil.getInitializer(astNode) != null) return;
|
||||
ASTNode lastChild = astNode.getLastChildNode();
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(lastChild);
|
||||
if (!(psiElement instanceof PsiErrorElement)) return;
|
||||
if (!((PsiErrorElement)psiElement).getErrorDescription().equals(JavaPsiBundle.message("expected.semicolon"))) return;
|
||||
// Impossible modifiers for a method
|
||||
if (BasicJavaAstTreeUtil.hasModifierProperty(astNode, JavaTokenType.TRANSIENT_KEYWORD) ||
|
||||
BasicJavaAstTreeUtil.hasModifierProperty(astNode, JavaTokenType.VOLATILE_KEYWORD)) {
|
||||
return;
|
||||
}
|
||||
ASTNode typeElement = BasicJavaAstTreeUtil.getTypeElement(astNode);
|
||||
if (typeElement == null || !typeElement.getText().equals("void")) return;
|
||||
int endOffset = astNode.getTextRange().getEndOffset();
|
||||
editor.getDocument().insertString(endOffset, "()");
|
||||
editor.getDocument().insertString(endOffset + 2, "{}");
|
||||
editor.getCaretModel().moveToOffset(endOffset + 1);
|
||||
processor.registerUnresolvedError(endOffset + 1);
|
||||
processor.setSkipEnter(true);
|
||||
return;
|
||||
}
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_METHOD))) return;
|
||||
if (!shouldMethodHaveBody(BasicJavaAstTreeUtil.toPsi(astNode))) return;
|
||||
|
||||
final ASTNode body = BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
final Document doc = editor.getDocument();
|
||||
if (body != null) {
|
||||
// See IDEADEV-1093. This is quite hacky heuristic but it seem to be best we can do.
|
||||
String bodyText = body.getText();
|
||||
if (bodyText.startsWith("{")) {
|
||||
final ASTNode statement = BasicJavaAstTreeUtil.findChildByType(body, STATEMENT_SET);
|
||||
if (statement != null) {
|
||||
if (BasicJavaAstTreeUtil.is(statement, BASIC_DECLARATION_STATEMENT)) {
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(statement);
|
||||
if (psiElement != null && PsiTreeUtil.getDeepestLast(psiElement) instanceof PsiErrorElement) {
|
||||
ASTNode containingClass = BasicJavaAstTreeUtil.getParentOfType(astNode, CLASS_SET);
|
||||
if (containingClass != null && BasicJavaAstTreeUtil.getRBrace(containingClass) == null) {
|
||||
doc.insertString(body.getTextRange().getStartOffset() + 1, "\n}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
ASTNode throwList = BasicJavaAstTreeUtil.findChildByType(astNode, BASIC_THROWS_LIST);
|
||||
if (throwList != null) {
|
||||
int endOffset = throwList.getTextRange().getEndOffset();
|
||||
if (endOffset < doc.getTextLength() && doc.getCharsSequence().charAt(endOffset) == ';') {
|
||||
doc.deleteString(endOffset, endOffset + 1);
|
||||
}
|
||||
processor.insertBracesWithNewLine(editor, endOffset);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldMethodHaveBody(@Nullable PsiElement method){
|
||||
return AfterSemicolonEnterProcessor.shouldHaveBody(
|
||||
BasicJavaAstTreeUtil.toNode(method));
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiJavaToken;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class MissingReturnExpressionFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_RETURN_STATEMENT))) {
|
||||
return;
|
||||
}
|
||||
if (!BasicJavaAstTreeUtil.hasErrorElements(astNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fixMethodCallWithoutTrailingSemicolon(astNode, editor, processor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASTNode returnValue = BasicJavaAstTreeUtil.getReturnValue(astNode);
|
||||
if (returnValue != null
|
||||
&& lineNumber(editor, editor.getCaretModel().getOffset()) == lineNumber(editor, returnValue.getTextRange().getStartOffset())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ASTNode parent = BasicJavaAstTreeUtil.getParentOfType(astNode, BasicJavaTokenSet.create(BASIC_CLASS_INITIALIZER, BASIC_METHOD));
|
||||
if (BasicJavaAstTreeUtil.is(parent, BASIC_METHOD)) {
|
||||
ASTNode type = BasicJavaAstTreeUtil.findChildByType(parent, BASIC_TYPE);
|
||||
if (type != null && !type.getText().equals("void")) {
|
||||
final int startOffset = astNode.getTextRange().getStartOffset();
|
||||
if (returnValue != null) {
|
||||
editor.getDocument().insertString(startOffset + "return".length(), ";");
|
||||
}
|
||||
|
||||
processor.registerUnresolvedError(startOffset + "return".length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean fixMethodCallWithoutTrailingSemicolon(@Nullable ASTNode returnStatement, @NotNull Editor editor,
|
||||
@NotNull AbstractBasicJavaSmartEnterProcessor processor) {
|
||||
if (returnStatement == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final ASTNode lastChild = returnStatement.getLastChildNode();
|
||||
if (!(BasicJavaAstTreeUtil.is(lastChild, TokenType.ERROR_ELEMENT))) {
|
||||
return false;
|
||||
}
|
||||
ASTNode prev = lastChild.getTreePrev();
|
||||
if (BasicJavaAstTreeUtil.isWhiteSpace(prev)) {
|
||||
prev = prev.getTreePrev();
|
||||
}
|
||||
|
||||
if (!(prev instanceof PsiJavaToken prevToken)) {
|
||||
int offset = returnStatement.getTextRange().getEndOffset();
|
||||
final PsiElement psiMethod =
|
||||
BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toPsi(returnStatement), BASIC_METHOD, true,
|
||||
BasicJavaTokenSet.create(BASIC_LAMBDA_EXPRESSION));
|
||||
ASTNode method = BasicJavaAstTreeUtil.toNode(psiMethod);
|
||||
ASTNode type = BasicJavaAstTreeUtil.findChildByType(method, BASIC_TYPE);
|
||||
if (method != null && type != null && type.getText().equals("void")) {
|
||||
offset = returnStatement.getTextRange().getStartOffset() + "return".length();
|
||||
}
|
||||
editor.getDocument().insertString(offset, ";");
|
||||
//processor.setSkipEnter(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (prevToken.getTokenType() == JavaTokenType.SEMICOLON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int offset = returnStatement.getTextRange().getEndOffset();
|
||||
editor.getDocument().insertString(offset, ";");
|
||||
if (prevToken.getTokenType() == JavaTokenType.RETURN_KEYWORD) {
|
||||
final ASTNode method = BasicJavaAstTreeUtil.getParentOfType(returnStatement, BASIC_METHOD);
|
||||
ASTNode type = BasicJavaAstTreeUtil.findChildByType(method, BASIC_TYPE);
|
||||
if (method != null && type != null && !type.getText().equals("void")) {
|
||||
editor.getCaretModel().moveToOffset(offset);
|
||||
processor.setSkipEnter(true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private static int lineNumber(Editor editor, int offset) {
|
||||
return editor.getDocument().getLineNumber(offset);
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SWITCH_EXPRESSION;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SWITCH_STATEMENT;
|
||||
|
||||
public class MissingSwitchBodyFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_EXPRESSION, BASIC_SWITCH_STATEMENT))) return;
|
||||
|
||||
final ASTNode body = BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
if (body != null) return;
|
||||
|
||||
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
|
||||
assert rParenth != null;
|
||||
|
||||
int offset = rParenth.getTextRange().getEndOffset();
|
||||
processor.insertBraces(editor, offset);
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SYNCHRONIZED_STATEMENT;
|
||||
|
||||
public class MissingSynchronizedBodyFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_SYNCHRONIZED_STATEMENT))) return;
|
||||
|
||||
ASTNode body = BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
if (body != null) return;
|
||||
|
||||
processor.insertBraces(editor, astNode.getTextRange().getEndOffset());
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_THROW_STATEMENT;
|
||||
|
||||
public class MissingThrowExpressionFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode)
|
||||
throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_THROW_STATEMENT)) {
|
||||
ASTNode expression = BasicJavaAstTreeUtil.getExpression(astNode);
|
||||
if (expression != null &&
|
||||
startLine(editor, astNode) == startLine(editor, expression)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int startOffset = astNode.getTextRange().getStartOffset();
|
||||
if (expression != null) {
|
||||
editor.getDocument().insertString(startOffset + "throw".length(), ";");
|
||||
}
|
||||
processor.registerUnresolvedError(startOffset + "throw".length());
|
||||
}
|
||||
}
|
||||
|
||||
private static int startLine(Editor editor, @NotNull ASTNode psiElement) {
|
||||
return editor.getDocument().getLineNumber(psiElement.getTextRange().getStartOffset());
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_TRY_STATEMENT;
|
||||
|
||||
public class MissingTryBodyFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_TRY_STATEMENT))) return;
|
||||
|
||||
ASTNode body = BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
if (body != null) return;
|
||||
|
||||
processor.insertBraces(editor, astNode.getTextRange().getEndOffset());
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ANNOTATION_PARAMETER_LIST;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_PARAMETER_LIST;
|
||||
|
||||
public class ParameterListFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_PARAMETER_LIST) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_ANNOTATION_PARAMETER_LIST)) {
|
||||
String text = astNode.getText();
|
||||
if (StringUtil.startsWithChar(text, '(') && !StringUtil.endsWithChar(text, ')')) {
|
||||
ASTNode[] params = BasicJavaAstTreeUtil.is(astNode, BASIC_PARAMETER_LIST) ?
|
||||
BasicJavaAstTreeUtil.getParameterListParameters(astNode) :
|
||||
BasicJavaAstTreeUtil.getAnnotationParameterListAttributes(astNode);
|
||||
int offset;
|
||||
if (params.length == 0) {
|
||||
offset = astNode.getTextRange().getStartOffset() + 1;
|
||||
}
|
||||
else {
|
||||
offset = params[params.length - 1].getTextRange().getEndOffset();
|
||||
}
|
||||
editor.getDocument().insertString(offset, ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_PARENTH_EXPRESSION;
|
||||
|
||||
public class ParenthesizedFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_PARENTH_EXPRESSION)) {
|
||||
final ASTNode lastChild = astNode.getLastChildNode();
|
||||
if (lastChild != null && !")".equals(lastChild.getText())) {
|
||||
editor.getDocument().insertString(astNode.getTextRange().getEndOffset(), ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionHandler;
|
||||
import com.intellij.openapi.editor.actionSystem.EditorActionManager;
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class PlainEnterProcessor implements ASTNodeEnterProcessor {
|
||||
|
||||
@Override
|
||||
public boolean doEnter(@NotNull Editor editor, @NotNull ASTNode astNode, boolean isModified) {
|
||||
if (expandCodeBlock(editor, astNode)) return true;
|
||||
|
||||
getEnterHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE).execute(editor, editor.getCaretModel().getCurrentCaret(),
|
||||
EditorUtil.getEditorDataContext(editor));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean expandCodeBlock(@NotNull Editor editor, @Nullable ASTNode astNode) {
|
||||
ASTNode block = getControlStatementBlock(editor.getCaretModel().getOffset(), astNode);
|
||||
PsiElement psiBlock = BasicJavaAstTreeUtil.toPsi(block);
|
||||
if (processExistingBlankLine(editor, psiBlock, astNode)) {
|
||||
return true;
|
||||
}
|
||||
if (block == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EditorActionHandler enterHandler = getEnterHandler(IdeActions.ACTION_EDITOR_START_NEW_LINE);
|
||||
ASTNode firstElement = BasicJavaAstTreeUtil.getFirstBodyElement(block);
|
||||
if (firstElement == null) {
|
||||
firstElement = BasicJavaAstTreeUtil.getRBrace(block);
|
||||
// Plain enter processor inserts enter after the end of line, hence, we don't want to use it here because the line ends with
|
||||
// the empty braces block. So, we get the following in case of default handler usage:
|
||||
// Before:
|
||||
// if (condition[caret]) {}
|
||||
// After:
|
||||
// if (condition) {}
|
||||
// [caret]
|
||||
enterHandler = getEnterHandler(IdeActions.ACTION_EDITOR_ENTER);
|
||||
}
|
||||
editor.getCaretModel().moveToOffset(firstElement != null ?
|
||||
firstElement.getTextRange().getStartOffset() :
|
||||
block.getTextRange().getEndOffset());
|
||||
enterHandler.execute(editor, editor.getCaretModel().getCurrentCaret(), EditorUtil.getEditorDataContext(editor));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static EditorActionHandler getEnterHandler(String actionId) {
|
||||
return EditorActionManager.getInstance().getActionHandler(actionId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static ASTNode getControlStatementBlock(int caret, ASTNode astNode) {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_TRY_STATEMENT)) {
|
||||
ASTNode tryBlock = BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
if (tryBlock != null && caret < tryBlock.getTextRange().getEndOffset()) return tryBlock;
|
||||
|
||||
for (ASTNode catchBlock : BasicJavaAstTreeUtil.getCatchBlocks(astNode)) {
|
||||
if (catchBlock != null && caret < catchBlock.getTextRange().getEndOffset()) return catchBlock;
|
||||
}
|
||||
|
||||
return BasicJavaAstTreeUtil.getFinallyBlock(astNode);
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_SYNCHRONIZED_STATEMENT)) {
|
||||
return BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_METHOD)) {
|
||||
ASTNode methodBody = BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
if (methodBody != null) return methodBody;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_STATEMENT)) {
|
||||
return BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
}
|
||||
|
||||
ASTNode body = null;
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_IF_STATEMENT)) {
|
||||
body = BasicJavaAstTreeUtil.getThenBranch(astNode);
|
||||
if (body != null && caret > body.getTextRange().getEndOffset()) {
|
||||
body = BasicJavaAstTreeUtil.getElseBranch(astNode);
|
||||
}
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_WHILE_STATEMENT)) {
|
||||
body = BasicJavaAstTreeUtil.getBlock(astNode);
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_FOR_STATEMENT)) {
|
||||
body = BasicJavaAstTreeUtil.getForBody(astNode);
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_FOREACH_STATEMENT)) {
|
||||
body = BasicJavaAstTreeUtil.getBlock(astNode);
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(astNode, BASIC_DO_WHILE_STATEMENT)) {
|
||||
body = BasicJavaAstTreeUtil.getDoWhileBody(astNode);
|
||||
}
|
||||
|
||||
return BasicJavaAstTreeUtil.is(body, BASIC_BLOCK_STATEMENT) ?
|
||||
BasicJavaAstTreeUtil.getCodeBlock(body) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* There is a possible case that target code block already starts with the empty line:
|
||||
* <pre>
|
||||
* void test(int i) {
|
||||
* if (i > 1[caret]) {
|
||||
*
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* We want just move caret to correct position at that empty line without creating additional empty line then.
|
||||
*
|
||||
* @param editor target editor
|
||||
* @param codeBlock target code block to which new empty line is going to be inserted
|
||||
* @param element target element under caret
|
||||
* @return {@code true} if it was found out that the given code block starts with the empty line and caret
|
||||
* is pointed to correct position there, i.e. no additional processing is required;
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
private static boolean processExistingBlankLine(@NotNull Editor editor, @Nullable PsiElement codeBlock, @Nullable ASTNode element) {
|
||||
PsiWhiteSpace whiteSpace = null;
|
||||
if (codeBlock == null) {
|
||||
PsiElement psiElement = BasicJavaAstTreeUtil.toPsi(element);
|
||||
if (psiElement != null && !(BasicJavaAstTreeUtil.is(element, MEMBER_SET))) {
|
||||
final PsiElement next = PsiTreeUtil.nextLeaf(psiElement);
|
||||
if (next instanceof PsiWhiteSpace) {
|
||||
whiteSpace = (PsiWhiteSpace)next;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
whiteSpace = PsiTreeUtil.findChildOfType(codeBlock, PsiWhiteSpace.class);
|
||||
if (whiteSpace == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PsiElement lbraceCandidate = whiteSpace.getPrevSibling();
|
||||
if (lbraceCandidate == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ASTNode node = lbraceCandidate.getNode();
|
||||
if (node == null || node.getElementType() != JavaTokenType.LBRACE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (whiteSpace == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final TextRange textRange = whiteSpace.getTextRange();
|
||||
final Document document = editor.getDocument();
|
||||
final CharSequence whiteSpaceText = document.getCharsSequence().subSequence(textRange.getStartOffset(), textRange.getEndOffset());
|
||||
if (StringUtil.countNewLines(whiteSpaceText) < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int i = CharArrayUtil.shiftForward(whiteSpaceText, 0, " \t");
|
||||
if (i >= whiteSpaceText.length() - 1) {
|
||||
assert false : String.format("code block: %s, white space: %s",
|
||||
codeBlock == null ? "undefined" : codeBlock.getTextRange(),
|
||||
whiteSpace.getTextRange());
|
||||
return false;
|
||||
}
|
||||
|
||||
editor.getCaretModel().moveToOffset(i + 1 + textRange.getStartOffset());
|
||||
EditorActionManager actionManager = EditorActionManager.getInstance();
|
||||
EditorActionHandler actionHandler = actionManager.getActionHandler(IdeActions.ACTION_EDITOR_MOVE_LINE_END);
|
||||
final DataContext dataContext = DataManager.getInstance().getDataContext(editor.getComponent());
|
||||
actionHandler.execute(editor, editor.getCaretModel().getCurrentCaret(), dataContext);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SWITCH_EXPRESSION;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_SWITCH_STATEMENT;
|
||||
|
||||
public class SwitchExpressionFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_EXPRESSION) ||
|
||||
BasicJavaAstTreeUtil.is(astNode, BASIC_SWITCH_STATEMENT)
|
||||
) {
|
||||
final Document doc = editor.getDocument();
|
||||
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
|
||||
final ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
|
||||
final ASTNode condition = BasicJavaAstTreeUtil.getExpression(astNode);
|
||||
|
||||
if (condition == null) {
|
||||
if (lParenth == null || rParenth == null) {
|
||||
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(astNode.getTextRange().getStartOffset()));
|
||||
final ASTNode block = BasicJavaAstTreeUtil.getCodeBlock(astNode);
|
||||
if (block != null) {
|
||||
stopOffset = Math.min(stopOffset, block.getTextRange().getStartOffset());
|
||||
}
|
||||
doc.replaceString(astNode.getTextRange().getStartOffset(), stopOffset, "switch ()");
|
||||
}
|
||||
else {
|
||||
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
|
||||
}
|
||||
}
|
||||
else if (rParenth == null) {
|
||||
doc.insertString(condition.getTextRange().getEndOffset(), ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorModificationUtilEx;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CONDITIONAL_EXPRESSION;
|
||||
|
||||
public class TernaryColonFixer implements Fixer {
|
||||
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (!(BasicJavaAstTreeUtil.is(astNode, BASIC_CONDITIONAL_EXPRESSION))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.getConditionalExpressionThenExpression(astNode) == null ||
|
||||
astNode.findChildByType(JavaTokenType.COLON) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
editor.getCaretModel().moveToOffset(astNode.getTextRange().getEndOffset());
|
||||
EditorModificationUtilEx.insertStringAtCaret(editor, ": ");
|
||||
processor.setSkipEnter(true);
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.smartEnter;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_WHILE_STATEMENT;
|
||||
|
||||
public class WhileConditionFixer implements Fixer {
|
||||
@Override
|
||||
public void apply(Editor editor, AbstractBasicJavaSmartEnterProcessor processor, @NotNull ASTNode astNode) throws IncorrectOperationException {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, BASIC_WHILE_STATEMENT)) {
|
||||
final Document doc = editor.getDocument();
|
||||
final ASTNode rParenth = BasicJavaAstTreeUtil.getRParenth(astNode);
|
||||
final ASTNode lParenth = BasicJavaAstTreeUtil.getLParenth(astNode);
|
||||
final ASTNode condition = BasicJavaAstTreeUtil.getWhileCondition(astNode);
|
||||
|
||||
if (condition == null) {
|
||||
if (lParenth == null || rParenth == null) {
|
||||
int stopOffset = doc.getLineEndOffset(doc.getLineNumber(astNode.getTextRange().getStartOffset()));
|
||||
final ASTNode block = BasicJavaAstTreeUtil.getWhileBody(astNode);
|
||||
if (block != null) {
|
||||
stopOffset = Math.min(stopOffset, block.getTextRange().getStartOffset());
|
||||
}
|
||||
stopOffset = Math.min(stopOffset, astNode.getTextRange().getEndOffset());
|
||||
|
||||
doc.replaceString(astNode.getTextRange().getStartOffset(), stopOffset, "while ()");
|
||||
processor.registerUnresolvedError(astNode.getTextRange().getStartOffset() + "while (".length());
|
||||
} else {
|
||||
processor.registerUnresolvedError(lParenth.getTextRange().getEndOffset());
|
||||
}
|
||||
} else if (rParenth == null) {
|
||||
doc.insertString(condition.getTextRange().getEndOffset(), ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.xml.XMLLanguage;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_BIT_SET;
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public abstract class AbstractBasicBackBasicSelectioner extends ExtendWordSelectionHandlerBase {
|
||||
|
||||
private static Predicate<PsiElement> getElementPredicate() {
|
||||
return (e) -> {
|
||||
Language language = e.getLanguage();
|
||||
return !(language instanceof XMLLanguage || language.isKindOf(XMLLanguage.INSTANCE));
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull final PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return
|
||||
!BasicJavaAstTreeUtil.is(node, TokenType.WHITE_SPACE) &&
|
||||
!BasicJavaAstTreeUtil.is(node, JAVA_COMMENT_BIT_SET) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_CODE_BLOCK) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_ARRAY_INITIALIZER_EXPRESSION) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_PARAMETER_LIST) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_EXPRESSION_LIST) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_BLOCK_STATEMENT) &&
|
||||
!BasicJavaAstTreeUtil.is(node, JAVA_CODE_REFERENCE_ELEMENT_SET) &&
|
||||
!(BasicJavaAstTreeUtil.isJavaToken(node) &&
|
||||
!BasicJavaAstTreeUtil.isKeyword(node)) &&
|
||||
!BasicJavaAstTreeUtil.is(node, DOC_TAG, DOC_SNIPPET_TAG, DOC_INLINE_TAG) &&
|
||||
getElementPredicate().test(e);
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandlerBase;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.lang.xml.XMLLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiComment;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_BIT_SET;
|
||||
|
||||
public class AntLikePropertySelectionHandler extends ExtendWordSelectionHandlerBase {
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
TextRange range = e.getTextRange();
|
||||
char prevLeftChar = ' ';
|
||||
for (int left = Math.min(cursorOffset, editor.getDocument().getTextLength() - 1); left >= range.getStartOffset(); left--) {
|
||||
char leftChar = editorText.charAt(left);
|
||||
if (leftChar == '}') return Collections.emptyList();
|
||||
if (leftChar == '$' && prevLeftChar == '{') {
|
||||
for (int right = cursorOffset; right < range.getEndOffset(); right++) {
|
||||
char rightChar = editorText.charAt(right);
|
||||
if (rightChar == '{') return Collections.emptyList();
|
||||
if (rightChar == '}') {
|
||||
return Arrays.asList(new TextRange(left + 2, right), new TextRange(left, right + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
prevLeftChar = leftChar;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
Language l = e.getLanguage();
|
||||
if (!(l.equals(JavaLanguage.INSTANCE)
|
||||
|| l.equals(XMLLanguage.INSTANCE))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.getParentOfType(BasicJavaAstTreeUtil.toNode(e), JAVA_COMMENT_BIT_SET) == null) {
|
||||
return true;
|
||||
}
|
||||
return PsiTreeUtil.getParentOfType(e, PsiComment.class) == null;
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.TokenType;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.DocumentUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class CaseStatementsSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return node != null &&
|
||||
BasicJavaAstTreeUtil.is(node.getTreeParent(), BASIC_CODE_BLOCK) &&
|
||||
BasicJavaAstTreeUtil.is(node.getTreeParent().getTreeParent(), BASIC_SWITCH_STATEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement elementStatement,
|
||||
@NotNull CharSequence editorText,
|
||||
int cursorOffset,
|
||||
@NotNull Editor editor) {
|
||||
ASTNode statement = BasicJavaAstTreeUtil.toNode(elementStatement);
|
||||
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
ASTNode caseStart = statement;
|
||||
ASTNode caseEnd = statement;
|
||||
|
||||
if (statement == null ||
|
||||
BasicJavaAstTreeUtil.is(statement, BASIC_SWITCH_STATEMENT)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
ASTNode labelStatement = BasicJavaAstTreeUtil.is(statement, BASIC_SWITCH_LABEL_STATEMENT) ? statement : null;
|
||||
ASTNode sibling;
|
||||
if (labelStatement == null) {
|
||||
sibling = statement.getTreePrev();
|
||||
while (sibling != null && !BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) {
|
||||
if (!BasicJavaAstTreeUtil.isWhiteSpace(sibling)) caseStart = sibling;
|
||||
sibling = sibling.getTreePrev();
|
||||
}
|
||||
labelStatement = sibling;
|
||||
}
|
||||
if (labelStatement != null) {
|
||||
ASTNode nextLabel = BasicJavaAstTreeUtil.skipSiblingsBackward(labelStatement, TokenType.WHITE_SPACE);
|
||||
while (BasicJavaAstTreeUtil.is(nextLabel, BASIC_SWITCH_LABEL_STATEMENT)) {
|
||||
labelStatement = nextLabel;
|
||||
nextLabel = BasicJavaAstTreeUtil.skipSiblingsBackward(labelStatement, TokenType.WHITE_SPACE);
|
||||
}
|
||||
}
|
||||
|
||||
sibling = BasicJavaAstTreeUtil.isWhiteSpace(statement) ? statement.getTreeNext() : statement;
|
||||
while (BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) {
|
||||
sibling = BasicJavaAstTreeUtil.skipSiblingsForward(sibling, TokenType.WHITE_SPACE);
|
||||
}
|
||||
while (sibling != null && !BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) {
|
||||
if (!BasicJavaAstTreeUtil.isWhiteSpace(sibling) &&
|
||||
!BasicJavaAstTreeUtil.isJavaToken(sibling) // end of switch
|
||||
) {
|
||||
caseEnd = sibling;
|
||||
}
|
||||
sibling = sibling.getTreeNext();
|
||||
}
|
||||
|
||||
Document document = editor.getDocument();
|
||||
|
||||
int endOffset =
|
||||
DocumentUtil.getLineEndOffset(BasicJavaAstTreeUtil.getTextOffset(caseEnd) + caseEnd.getTextLength(), document) + 1;
|
||||
|
||||
if (!BasicJavaAstTreeUtil.is(caseStart, BASIC_SWITCH_LABEL_STATEMENT)) {
|
||||
result.add(new TextRange(DocumentUtil.getLineStartOffset(BasicJavaAstTreeUtil.getTextOffset(caseStart), document),
|
||||
endOffset));
|
||||
}
|
||||
if (labelStatement != null) {
|
||||
result.add(
|
||||
new TextRange(DocumentUtil.getLineStartOffset(BasicJavaAstTreeUtil.getTextOffset(labelStatement), document),
|
||||
endOffset));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class CodeBlockOrInitializerSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return BasicJavaAstTreeUtil.is(node, BASIC_CODE_BLOCK) ||
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_ARRAY_INITIALIZER_EXPRESSION) ||
|
||||
BasicJavaAstTreeUtil.is(node, CLASS_SET) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_TYPE_PARAMETER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
result.add(getElementRange(node));
|
||||
|
||||
|
||||
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
|
||||
if (!children.isEmpty()) {
|
||||
int start = findOpeningBrace(children);
|
||||
|
||||
// in non-Java PsiClasses, there can be no opening brace
|
||||
if (start != 0) {
|
||||
int end = findClosingBrace(children, start);
|
||||
result.addAll(expandToWholeLine(editorText, new TextRange(start, end)));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public TextRange getElementRange(@NotNull ASTNode astNode) {
|
||||
if (BasicJavaAstTreeUtil.is(astNode, CLASS_SET)) {
|
||||
ASTNode lBrace = BasicJavaAstTreeUtil.getLBrace(astNode);
|
||||
ASTNode rBrace = BasicJavaAstTreeUtil.getRBrace(astNode);
|
||||
if (lBrace != null && rBrace != null) {
|
||||
return new TextRange(BasicJavaAstTreeUtil.getTextOffset(lBrace), rBrace.getTextRange().getEndOffset());
|
||||
}
|
||||
}
|
||||
|
||||
return astNode.getTextRange();
|
||||
}
|
||||
|
||||
public static int findOpeningBrace(List<ASTNode> children) {
|
||||
int start = 0;
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
ASTNode child = children.get(i);
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.LBRACE)) {
|
||||
int j = i + 1;
|
||||
|
||||
while (BasicJavaAstTreeUtil.isWhiteSpace(children.get(j))) {
|
||||
j++;
|
||||
}
|
||||
|
||||
start = children.get(j).getTextRange().getStartOffset();
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
public static int findClosingBrace(List<ASTNode> children, int startOffset) {
|
||||
int end = children.get(children.size() - 1).getTextRange().getEndOffset();
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
ASTNode child = children.get(i);
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.RBRACE)) {
|
||||
int j = i - 1;
|
||||
|
||||
while (BasicJavaAstTreeUtil.isWhiteSpace(children.get(j)) && children.get(j).getTextRange().getStartOffset() > startOffset) {
|
||||
j--;
|
||||
}
|
||||
|
||||
end = children.get(j).getTextRange().getEndOffset();
|
||||
}
|
||||
}
|
||||
return end;
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaDocTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.DOC_COMMENT;
|
||||
|
||||
public class DocCommentSelectioner extends LineCommentSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), DOC_COMMENT);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
|
||||
|
||||
int startOffset = e.getTextRange().getStartOffset();
|
||||
int endOffset = e.getTextRange().getEndOffset();
|
||||
|
||||
for (ASTNode child : children) {
|
||||
if (BasicJavaAstTreeUtil.is(child, JavaDocTokenType.DOC_COMMENT_DATA)) {
|
||||
char[] chars = child.getText().toCharArray();
|
||||
|
||||
if (CharArrayUtil.shiftForward(chars, 0, " *\n\t\r") != chars.length) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
startOffset = child.getTextRange().getEndOffset();
|
||||
}
|
||||
|
||||
for (ASTNode child : children) {
|
||||
if (BasicJavaAstTreeUtil.is(child, JavaDocTokenType.DOC_COMMENT_DATA)) {
|
||||
char[] chars = child.getText().toCharArray();
|
||||
|
||||
if (CharArrayUtil.shiftForward(chars, 0, " *\n\t\r") != chars.length) {
|
||||
endOffset = child.getTextRange().getEndOffset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
startOffset = CharArrayUtil.shiftBackward(editorText, startOffset - 1, "* \t") + 1;
|
||||
|
||||
result.add(new TextRange(startOffset, endOffset));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaDocTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
|
||||
|
||||
public class DocTagSelectioner extends WordSelectioner {
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return BasicJavaAstTreeUtil.is(node, DOC_TAG, DOC_SNIPPET_TAG, DOC_INLINE_TAG);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
result.add(getDocTagRange(e, editorText, cursorOffset));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static TextRange getDocTagRange(@NotNull PsiElement e, @NotNull CharSequence documentText, int minOffset) {
|
||||
TextRange range = e.getTextRange();
|
||||
|
||||
int endOffset = range.getEndOffset();
|
||||
int startOffset = range.getStartOffset();
|
||||
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
|
||||
|
||||
for (int i = children.size() - 1; i >= 0; i--) {
|
||||
ASTNode child = children.get(i);
|
||||
|
||||
int childStartOffset = child.getTextRange().getStartOffset();
|
||||
|
||||
if (childStartOffset <= minOffset) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.isDocToken(child)) {
|
||||
CharSequence chars = child.getChars();
|
||||
int shift = CharArrayUtil.shiftForward(chars, 0, " \t\n\r");
|
||||
|
||||
if (shift != chars.length() && !BasicJavaAstTreeUtil.is(child, JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!(BasicJavaAstTreeUtil.isWhiteSpace(child))) {
|
||||
break;
|
||||
}
|
||||
|
||||
endOffset = Math.min(childStartOffset, endOffset);
|
||||
}
|
||||
|
||||
startOffset = CharArrayUtil.shiftBackward(documentText, startOffset - 1, "* \t") + 1;
|
||||
|
||||
return new TextRange(startOffset, endOffset);
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_ENUM_CONSTANT;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_FIELD;
|
||||
|
||||
public class FieldSelectioner extends WordSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_FIELD, BASIC_ENUM_CONSTANT) &&
|
||||
e.getLanguage() == JavaLanguage.INSTANCE;
|
||||
}
|
||||
|
||||
private static void addRangeElem(final List<? super TextRange> result,
|
||||
CharSequence editorText,
|
||||
final ASTNode first,
|
||||
final int end) {
|
||||
if (first != null) {
|
||||
result.addAll(expandToWholeLine(editorText,
|
||||
new TextRange(first.getTextRange().getStartOffset(), end)));
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
TextRange fieldRange = node.getTextRange();
|
||||
ASTNode nameId = BasicJavaAstTreeUtil.getNameIdentifier(node);
|
||||
if (nameId == null) return new ArrayList<>();
|
||||
TextRange nameRange = nameId.getTextRange();
|
||||
ASTNode last = BasicJavaAstTreeUtil.getInitializer(node);
|
||||
int end = last == null ? nameRange.getEndOffset() : last.getTextRange().getEndOffset();
|
||||
|
||||
ASTNode comment = BasicJavaAstTreeUtil.getDocComment(node);
|
||||
if (comment != null) {
|
||||
TextRange commentTextRange = comment.getTextRange();
|
||||
addRangeElem(result, editorText, comment, commentTextRange.getEndOffset());
|
||||
}
|
||||
addRangeElem(result, editorText, nameId, end);
|
||||
addRangeElem(result, editorText, BasicJavaAstTreeUtil.getTypeElement(node), nameRange.getEndOffset());
|
||||
addRangeElem(result, editorText, BasicJavaAstTreeUtil.getModifierList(node), fieldRange.getEndOffset());
|
||||
result.addAll(expandToWholeLine(editorText, fieldRange));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_TRY_STATEMENT;
|
||||
|
||||
public class FinallyBlockSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), JavaTokenType.FINALLY_KEYWORD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
final ASTNode parent = node.getTreeParent();
|
||||
if (BasicJavaAstTreeUtil.is(parent, BASIC_TRY_STATEMENT)) {
|
||||
final ASTNode finallyBlock = BasicJavaAstTreeUtil.getFinallyBlock(parent);
|
||||
if (finallyBlock != null) {
|
||||
result.add(new TextRange(e.getTextRange().getStartOffset(), finallyBlock.getTextRange().getEndOffset()));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandler;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_FOREACH_STATEMENT;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_FOR_STATEMENT;
|
||||
|
||||
public class ForStatementHeaderSelectioner implements ExtendWordSelectionHandler {
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_FOR_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_FOREACH_STATEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ASTNode lParen = BasicJavaAstTreeUtil.getLParenth(node);
|
||||
ASTNode rParen = BasicJavaAstTreeUtil.getRParenth(node);
|
||||
if (lParen == null || rParen == null) return null;
|
||||
TextRange result = new TextRange(lParen.getTextRange().getEndOffset(), rParen.getTextRange().getStartOffset());
|
||||
return result.containsOffset(cursorOffset) ? Collections.singletonList(result) : null;
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_IF_STATEMENT;
|
||||
|
||||
public class IfStatementSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_IF_STATEMENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
List<TextRange> result = new ArrayList<>(expandToWholeLine(editorText, e.getTextRange(), false));
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
final ASTNode elseKeyword = BasicJavaAstTreeUtil.getElseElement(node);
|
||||
if (elseKeyword != null) {
|
||||
final ASTNode then = BasicJavaAstTreeUtil.getThenBranch(node);
|
||||
if (then != null) {
|
||||
final TextRange thenRange = new TextRange(e.getTextRange().getStartOffset(), then.getTextRange().getEndOffset());
|
||||
if (thenRange.contains(cursorOffset)) {
|
||||
result.addAll(expandToWholeLine(editorText, thenRange, false));
|
||||
}
|
||||
}
|
||||
|
||||
result.addAll(expandToWholeLine(editorText,
|
||||
new TextRange(elseKeyword.getTextRange().getStartOffset(),
|
||||
node.getTextRange().getEndOffset()),
|
||||
false));
|
||||
|
||||
final ASTNode branch = BasicJavaAstTreeUtil.getElseBranch(node);
|
||||
if (BasicJavaAstTreeUtil.is(branch, BASIC_IF_STATEMENT)) {
|
||||
final ASTNode element = BasicJavaAstTreeUtil.getElseElement(branch);
|
||||
if (element != null) {
|
||||
final ASTNode elseThen = BasicJavaAstTreeUtil.getThenBranch(branch);
|
||||
if (elseThen != null) {
|
||||
result.addAll(expandToWholeLine(editorText,
|
||||
new TextRange(elseKeyword.getTextRange().getStartOffset(),
|
||||
elseThen.getTextRange().getEndOffset()),
|
||||
false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.psi.JavaDocTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
|
||||
public class JavaBasicWordSelectionFilter implements Condition<PsiElement> {
|
||||
|
||||
public JavaBasicWordSelectionFilter() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean value(final PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return !BasicJavaAstTreeUtil.is(node, BASIC_CODE_BLOCK) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_ARRAY_INITIALIZER_EXPRESSION) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_PARAMETER_LIST) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_EXPRESSION_LIST) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_BLOCK_STATEMENT) &&
|
||||
!BasicJavaAstTreeUtil.is(node, JAVA_CODE_REFERENCE_ELEMENT_SET) &&
|
||||
!BasicJavaAstTreeUtil.isJavaToken(node) &&
|
||||
!BasicJavaAstTreeUtil.is(node, DOC_TAG, DOC_SNIPPET_TAG, DOC_INLINE_TAG) &&
|
||||
!(BasicJavaAstTreeUtil.isDocToken(node) &&
|
||||
BasicJavaAstTreeUtil.is(node, JavaDocTokenType.DOC_COMMENT_DATA));
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_CODE_BLOCK;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.CLASS_SET;
|
||||
|
||||
public class JavaTokenSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return BasicJavaAstTreeUtil.isJavaToken(node) && !BasicJavaAstTreeUtil.isKeyword(node) &&
|
||||
!BasicJavaAstTreeUtil.is(node.getTreeParent(), BASIC_CODE_BLOCK) &&
|
||||
!BasicJavaAstTreeUtil.is(node.getTreeParent(), CLASS_SET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!BasicJavaAstTreeUtil.is(node, JavaTokenType.SEMICOLON) &&
|
||||
!BasicJavaAstTreeUtil.is(node, JavaTokenType.LPARENTH)) {
|
||||
return super.select(e, editorText, cursorOffset, editor);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class JavaWordSelectioner extends AbstractWordSelectioner {
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (BasicJavaAstTreeUtil.isKeyword(node)) {
|
||||
return true;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.isJavaToken(node)) {
|
||||
return BasicJavaAstTreeUtil.is(node, JavaTokenType.IDENTIFIER) || BasicJavaAstTreeUtil.is(node, JavaTokenType.STRING_LITERAL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
List<TextRange> ranges = super.select(e, editorText, cursorOffset, editor);
|
||||
if (ranges == null) {
|
||||
return null;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), JavaTokenType.STRING_LITERAL)) {
|
||||
killRangesBreakingEscapes(e, ranges, e.getTextRange());
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
private static void killRangesBreakingEscapes(PsiElement e, List<TextRange> ranges, TextRange literalRange) {
|
||||
for (Iterator<TextRange> iterator = ranges.iterator(); iterator.hasNext(); ) {
|
||||
TextRange each = iterator.next();
|
||||
if (literalRange.contains(each) &&
|
||||
literalRange.getStartOffset() < each.getStartOffset() &&
|
||||
e.getText().charAt(each.getStartOffset() - literalRange.getStartOffset() - 1) == '\\') {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class ListSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return BasicJavaAstTreeUtil.is(node, BASIC_PARAMETER_LIST) ||
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_EXPRESSION_LIST) ||
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_RECORD_HEADER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
|
||||
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
|
||||
for (ASTNode child : children) {
|
||||
if (BasicJavaAstTreeUtil.isJavaToken(child)) {
|
||||
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.LPARENTH)) {
|
||||
start = BasicJavaAstTreeUtil.getTextOffset(child) + 1;
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.RPARENTH)) {
|
||||
end = BasicJavaAstTreeUtil.getTextOffset(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
if (start != 0 && end != 0) {
|
||||
result.add(new TextRange(start, end));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.SelectWordUtil;
|
||||
import com.intellij.lang.ASTNode;
|
||||
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 org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.STRING_LITERALS;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_LITERAL_EXPRESSION;
|
||||
|
||||
public class LiteralSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
PsiElement parent = e.getParent();
|
||||
return isStringLiteral(e) || isStringLiteral(parent);
|
||||
}
|
||||
|
||||
private static boolean isStringLiteral(PsiElement element) {
|
||||
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(element), STRING_LITERALS)
|
||||
&& element.getText().startsWith("\"")
|
||||
&& element.getText().endsWith("\"");
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
TextRange range = node.getTextRange();
|
||||
SelectWordUtil.addWordHonoringEscapeSequences(editorText, range, cursorOffset,
|
||||
new StringLiteralLexer('\"', JavaTokenType.STRING_LITERAL),
|
||||
result);
|
||||
ASTNode literalExpression = null;
|
||||
if (BasicJavaAstTreeUtil.is(node, BASIC_LITERAL_EXPRESSION)) {
|
||||
literalExpression = node;
|
||||
}
|
||||
if (literalExpression == null) {
|
||||
ASTNode parent = node.getTreeParent();
|
||||
if (BasicJavaAstTreeUtil.is(parent, BASIC_LITERAL_EXPRESSION)) {
|
||||
literalExpression = parent;
|
||||
}
|
||||
}
|
||||
PsiElement literalPsiExpression = BasicJavaAstTreeUtil.toPsi(literalExpression);
|
||||
if (literalExpression != null && literalPsiExpression != null && BasicJavaAstTreeUtil.isTextBlock(literalExpression)) {
|
||||
int contentStart = StringUtil.indexOf(editorText, '\n', range.getStartOffset());
|
||||
if (contentStart == -1) return result;
|
||||
contentStart += 1;
|
||||
int indent = BasicLiteralUtil.getTextBlockIndent(literalPsiExpression);
|
||||
if (indent == -1) return result;
|
||||
for (int i = 0; i < indent; i++) {
|
||||
if (editorText.charAt(contentStart + i) == '\n') return result;
|
||||
}
|
||||
int start = contentStart + indent;
|
||||
int end = range.getEndOffset() - 4;
|
||||
for (; end >= start; end--) {
|
||||
char c = editorText.charAt(end);
|
||||
if (c == '\n') break;
|
||||
if (!Character.isWhitespace(c)) {
|
||||
end += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (start < end) result.add(new TextRange(start, end));
|
||||
}
|
||||
else {
|
||||
result.add(new TextRange(range.getStartOffset() + 1, range.getEndOffset() - 1));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.codeInsight.editorActions.ExtendWordSelectionHandler;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_METHOD_CALL_EXPRESSION;
|
||||
|
||||
public class MethodCallSelectioner implements ExtendWordSelectionHandler {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_METHOD_CALL_EXPRESSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
ASTNode methodExpression = BasicJavaAstTreeUtil.getMethodExpression(node);
|
||||
if (methodExpression == null) {
|
||||
return null;
|
||||
}
|
||||
ASTNode referenceNameElement = BasicJavaAstTreeUtil.getReferenceNameElement(methodExpression);
|
||||
if (referenceNameElement == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return Arrays.asList(new TextRange(referenceNameElement.getTextRange().getStartOffset(), e.getTextRange().getEndOffset()),
|
||||
e.getTextRange());
|
||||
}
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_BIT_SET;
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.DOC_COMMENT;
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class MethodOrClassSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return (
|
||||
BasicJavaAstTreeUtil.is(node, CLASS_SET) &&
|
||||
!BasicJavaAstTreeUtil.is(node, BASIC_TYPE_PARAMETER) ||
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_METHOD)) &&
|
||||
e.getLanguage() == JavaLanguage.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return result;
|
||||
}
|
||||
ASTNode firstChild = node.getFirstChildNode();
|
||||
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(node);
|
||||
int i = 1;
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(firstChild, DOC_COMMENT)) {
|
||||
while (BasicJavaAstTreeUtil.isWhiteSpace(children.get(i))) {
|
||||
i++;
|
||||
}
|
||||
|
||||
TextRange range = new TextRange(children.get(i).getTextRange().getStartOffset(), e.getTextRange().getEndOffset());
|
||||
result.add(range);
|
||||
result.addAll(expandToWholeLinesWithBlanks(editorText, range));
|
||||
|
||||
range = firstChild.getTextRange();
|
||||
result.addAll(expandToWholeLinesWithBlanks(editorText, range));
|
||||
|
||||
firstChild = children.get(i++);
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(firstChild, JAVA_COMMENT_BIT_SET)) {
|
||||
while (BasicJavaAstTreeUtil.is(children.get(i), JAVA_COMMENT_OR_WHITESPACE_BIT_SET)) {
|
||||
i++;
|
||||
}
|
||||
ASTNode last = BasicJavaAstTreeUtil.isWhiteSpace(children.get(i - 1)) ? children.get(i - 2) : children.get(i - 1);
|
||||
TextRange range = new TextRange(firstChild.getTextRange().getStartOffset(), last.getTextRange().getEndOffset());
|
||||
if (range.contains(cursorOffset)) {
|
||||
result.addAll(expandToWholeLinesWithBlanks(editorText, range));
|
||||
}
|
||||
|
||||
range = new TextRange(children.get(i).getTextRange().getStartOffset(), e.getTextRange().getEndOffset());
|
||||
result.add(range);
|
||||
result.addAll(expandToWholeLinesWithBlanks(editorText, range));
|
||||
}
|
||||
|
||||
result.add(node.getTextRange());
|
||||
result.addAll(expandToWholeLinesWithBlanks(editorText, node.getTextRange()));
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(node, CLASS_SET)) {
|
||||
result.addAll(selectWithTypeParameters(node));
|
||||
result.addAll(selectBetweenBracesLines(children, editorText));
|
||||
}
|
||||
if (BasicJavaAstTreeUtil.is(node, BASIC_ANONYMOUS_CLASS)) {
|
||||
result.addAll(selectWholeBlock(node));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Collection<TextRange> selectWithTypeParameters(@NotNull ASTNode astClass) {
|
||||
final ASTNode identifier = BasicJavaAstTreeUtil.getNameIdentifier(astClass);
|
||||
final ASTNode list = BasicJavaAstTreeUtil.getTypeParameterList(astClass);
|
||||
if (identifier != null && list != null) {
|
||||
return Collections.singletonList(new TextRange(identifier.getTextRange().getStartOffset(), list.getTextRange().getEndOffset()));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static Collection<TextRange> selectBetweenBracesLines(List<ASTNode> children,
|
||||
@NotNull CharSequence editorText) {
|
||||
int start = CodeBlockOrInitializerSelectioner.findOpeningBrace(children);
|
||||
// in non-Java PsiClasses, there can be no opening brace
|
||||
if (start != 0) {
|
||||
int end = CodeBlockOrInitializerSelectioner.findClosingBrace(children, start);
|
||||
|
||||
return expandToWholeLinesWithBlanks(editorText, new TextRange(start, end));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static Collection<TextRange> selectWholeBlock(ASTNode clazz) {
|
||||
ASTNode lBrace = BasicJavaAstTreeUtil.getLBrace(clazz);
|
||||
ASTNode rBrace = BasicJavaAstTreeUtil.getRBrace(clazz);
|
||||
if (lBrace != null && rBrace != null) {
|
||||
return Collections.singleton(new TextRange(lBrace.getTextRange().getStartOffset(), rBrace.getTextRange().getEndOffset()));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class ReferenceSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
return BasicJavaAstTreeUtil.is(node, JAVA_CODE_REFERENCE_ELEMENT_SET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
ASTNode endElement = node;
|
||||
if (endElement == null) {
|
||||
return null;
|
||||
}
|
||||
while (BasicJavaAstTreeUtil.is(endElement, JAVA_CODE_REFERENCE_ELEMENT_SET) &&
|
||||
endElement.getTreeNext() != null) {
|
||||
endElement = endElement.getTreeNext();
|
||||
}
|
||||
|
||||
if (!(BasicJavaAstTreeUtil.is(endElement, JAVA_CODE_REFERENCE_ELEMENT_SET)) &&
|
||||
!(BasicJavaAstTreeUtil.is(endElement.getTreePrev(), REFERENCE_EXPRESSION_SET) &&
|
||||
BasicJavaAstTreeUtil.is(endElement, BASIC_EXPRESSION_LIST))) {
|
||||
endElement = endElement.getTreePrev();
|
||||
}
|
||||
|
||||
ASTNode element = node;
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
while (BasicJavaAstTreeUtil.is(element, JAVA_CODE_REFERENCE_ELEMENT_SET)) {
|
||||
ASTNode firstChild = element.getFirstChildNode();
|
||||
|
||||
ASTNode referenceName = BasicJavaAstTreeUtil.getReferenceNameElement(element);
|
||||
if (referenceName != null) {
|
||||
result.addAll(expandToWholeLine(editorText, new TextRange(referenceName.getTextRange().getStartOffset(),
|
||||
endElement.getTextRange().getEndOffset())));
|
||||
if (BasicJavaAstTreeUtil.is(endElement, JAVA_CODE_REFERENCE_ELEMENT_SET)) {
|
||||
final ASTNode endReferenceName = BasicJavaAstTreeUtil.getReferenceNameElement(endElement);
|
||||
if (endReferenceName != null) {
|
||||
result.addAll(expandToWholeLine(editorText, new TextRange(referenceName.getTextRange().getStartOffset(),
|
||||
endReferenceName.getTextRange().getEndOffset())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstChild == null) break;
|
||||
element = firstChild;
|
||||
}
|
||||
|
||||
TextRange range = new TextRange(element.getTextRange().getStartOffset(),
|
||||
endElement.getTextRange().getEndOffset());
|
||||
result.add(range);
|
||||
result.addAll(expandToWholeLine(editorText, range));
|
||||
|
||||
if (!(BasicJavaAstTreeUtil.is(node.getTreeParent(), JAVA_CODE_REFERENCE_ELEMENT_SET))) {
|
||||
if (BasicJavaAstTreeUtil.isJavaToken(node.getTreeNext()) ||
|
||||
BasicJavaAstTreeUtil.isWhiteSpace(node.getTreeNext()) ||
|
||||
BasicJavaAstTreeUtil.is(node.getTreeNext(), BASIC_EXPRESSION_LIST)) {
|
||||
List<TextRange> superSelect = super.select(e, editorText, cursorOffset, editor);
|
||||
if (superSelect != null) {
|
||||
result.addAll(superSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.LineTokenizer;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.*;
|
||||
|
||||
public class StatementGroupSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), STATEMENT_SET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
List<TextRange> result = new ArrayList<>();
|
||||
|
||||
PsiElement parentElement = e.getParent();
|
||||
ASTNode node = BasicJavaAstTreeUtil.toNode(e);
|
||||
if (node == null) {
|
||||
return result;
|
||||
}
|
||||
ASTNode parentNode = BasicJavaAstTreeUtil.toNode(parentElement);
|
||||
if (!BasicJavaAstTreeUtil.is(parentNode, BASIC_CODE_BLOCK) &&
|
||||
!BasicJavaAstTreeUtil.is(parentNode, BASIC_BLOCK_STATEMENT) ||
|
||||
BasicJavaAstTreeUtil.is(node, BASIC_SWITCH_LABEL_STATEMENT)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ASTNode startElement = node;
|
||||
ASTNode endElement = node;
|
||||
|
||||
|
||||
while (startElement.getTreePrev() != null) {
|
||||
ASTNode sibling = startElement.getTreePrev();
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(sibling, JavaTokenType.LBRACE)) break;
|
||||
|
||||
if (BasicJavaAstTreeUtil.isWhiteSpace(sibling)) {
|
||||
String[] strings = LineTokenizer.tokenize(sibling.getText().toCharArray(), false);
|
||||
if (strings.length > 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) break;
|
||||
|
||||
startElement = sibling;
|
||||
}
|
||||
|
||||
while (BasicJavaAstTreeUtil.isWhiteSpace(startElement)) {
|
||||
startElement = startElement.getTreeNext();
|
||||
}
|
||||
|
||||
while (endElement.getTreeNext() != null) {
|
||||
ASTNode sibling = endElement.getTreeNext();
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(sibling, JavaTokenType.RBRACE)) break;
|
||||
|
||||
if (BasicJavaAstTreeUtil.isWhiteSpace(sibling)) {
|
||||
String[] strings = LineTokenizer.tokenize(sibling.getText().toCharArray(), false);
|
||||
if (strings.length > 2) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (BasicJavaAstTreeUtil.is(sibling, BASIC_SWITCH_LABEL_STATEMENT)) break;
|
||||
|
||||
endElement = sibling;
|
||||
}
|
||||
|
||||
while (BasicJavaAstTreeUtil.isWhiteSpace(endElement)) {
|
||||
endElement = endElement.getTreePrev();
|
||||
}
|
||||
|
||||
result.addAll(expandToWholeLine(editorText, new TextRange(startElement.getTextRange().getStartOffset(),
|
||||
endElement.getTextRange().getEndOffset())));
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.editorActions.wordSelection;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaElementType.BASIC_TYPE_CAST_EXPRESSION;
|
||||
|
||||
public class TypeCastSelectioner extends AbstractBasicBackBasicSelectioner {
|
||||
|
||||
@Override
|
||||
public boolean canSelect(@NotNull PsiElement e) {
|
||||
return BasicJavaAstTreeUtil.is(BasicJavaAstTreeUtil.toNode(e), BASIC_TYPE_CAST_EXPRESSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TextRange> select(@NotNull PsiElement e, @NotNull CharSequence editorText, int cursorOffset, @NotNull Editor editor) {
|
||||
List<TextRange> result = new ArrayList<>(expandToWholeLine(editorText, e.getTextRange(), false));
|
||||
|
||||
List<ASTNode> children = BasicJavaAstTreeUtil.getChildren(BasicJavaAstTreeUtil.toNode(e));
|
||||
ASTNode lParen = null;
|
||||
ASTNode rParen = null;
|
||||
for (ASTNode child : children) {
|
||||
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.LPARENTH)) lParen = child;
|
||||
if (BasicJavaAstTreeUtil.is(child, JavaTokenType.RPARENTH)) rParen = child;
|
||||
}
|
||||
|
||||
if (lParen != null && rParen != null) {
|
||||
result.addAll(expandToWholeLine(editorText,
|
||||
new TextRange(lParen.getTextRange().getStartOffset(),
|
||||
rParen.getTextRange().getEndOffset()),
|
||||
false));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.highlighting;
|
||||
|
||||
import com.intellij.codeInsight.hint.DeclarationRangeUtil;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.BracePair;
|
||||
import com.intellij.lang.PairedBraceMatcher;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.JavaDocTokenType;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.java.IJavaElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.*;
|
||||
|
||||
public class JavaBraceMatcher implements PairedBraceMatcher {
|
||||
private final BracePair[] pairs = new BracePair[] {
|
||||
new BracePair(JavaTokenType.LPARENTH, JavaTokenType.RPARENTH, false),
|
||||
new BracePair(JavaTokenType.LBRACE, JavaTokenType.RBRACE, true),
|
||||
new BracePair(JavaTokenType.LBRACKET, JavaTokenType.RBRACKET, false),
|
||||
new BracePair(JavaDocTokenType.DOC_INLINE_TAG_START, JavaDocTokenType.DOC_INLINE_TAG_END, false),
|
||||
new BracePair(JavaTokenType.LT, JavaTokenType.GT, false)
|
||||
};
|
||||
public JavaBraceMatcher() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public BracePair @NotNull [] getPairs() {
|
||||
return pairs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPairedBracesAllowedBeforeType(@NotNull final IElementType lbraceType, @Nullable final IElementType contextType) {
|
||||
if (contextType instanceof IJavaElementType) return isPairedBracesAllowedBeforeTypeInJava(contextType);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isPairedBracesAllowedBeforeTypeInJava(final IElementType tokenType) {
|
||||
return JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(tokenType)
|
||||
|| tokenType == JavaTokenType.SEMICOLON
|
||||
|| tokenType == JavaTokenType.COMMA
|
||||
|| tokenType == JavaTokenType.RPARENTH
|
||||
|| tokenType == JavaTokenType.RBRACKET
|
||||
|| tokenType == JavaTokenType.RBRACE
|
||||
|| tokenType == JavaTokenType.LBRACE
|
||||
|| tokenType == JavaTokenType.DOT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCodeConstructStart(final PsiFile file, int openingBraceOffset) {
|
||||
PsiElement element = file.findElementAt(openingBraceOffset);
|
||||
if (element == null || element instanceof PsiFile) return openingBraceOffset;
|
||||
PsiElement parent = element.getParent();
|
||||
if(parent==null) return openingBraceOffset;
|
||||
ASTNode parentNode = parent.getNode();
|
||||
if (BasicJavaAstTreeUtil.is(parentNode, BASIC_CODE_BLOCK)) {
|
||||
parentNode = parentNode.getTreeParent();
|
||||
if (BasicJavaAstTreeUtil.is(parentNode, BASIC_METHOD) ||
|
||||
BasicJavaAstTreeUtil.is(parentNode, BASIC_CLASS_INITIALIZER)) {
|
||||
TextRange range = DeclarationRangeUtil.getDeclarationRange(parentNode.getPsi());
|
||||
return range.getStartOffset();
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(parentNode, JAVA_STATEMENT_BIT_SET)) {
|
||||
if (BasicJavaAstTreeUtil.is(parentNode, BASIC_BLOCK_STATEMENT) &&
|
||||
BasicJavaAstTreeUtil.is(parentNode.getTreeParent(), JAVA_STATEMENT_BIT_SET)) {
|
||||
parentNode = parentNode.getTreeParent();
|
||||
}
|
||||
return parentNode.getTextRange().getStartOffset();
|
||||
}
|
||||
}
|
||||
else if (BasicJavaAstTreeUtil.is(parentNode, CLASS_KEYWORD_BIT_SET)) {
|
||||
TextRange range = DeclarationRangeUtil.getDeclarationRange(parent);
|
||||
return range.getStartOffset();
|
||||
}
|
||||
return openingBraceOffset;
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.intellij.codeInsight.highlighting;
|
||||
|
||||
import com.intellij.BaseJavaJspElementType;
|
||||
import com.intellij.ide.highlighter.JavaFileType;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.impl.source.BasicElementTypes;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class JavaPairedBraceMatcher extends PairedBraceAndAnglesMatcher {
|
||||
|
||||
private static class Holder {
|
||||
private static final BasicJavaTokenSet TYPE_TOKENS =
|
||||
BasicJavaTokenSet.orSet(BaseJavaJspElementType.WHITE_SPACE_BIT_SET,
|
||||
BasicElementTypes.JAVA_COMMENT_BIT_SET,
|
||||
BasicJavaTokenSet.create(JavaTokenType.IDENTIFIER, JavaTokenType.COMMA,
|
||||
JavaTokenType.AT,//anno
|
||||
JavaTokenType.RBRACKET, JavaTokenType.LBRACKET, //arrays
|
||||
JavaTokenType.QUEST, JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.SUPER_KEYWORD));//wildcards
|
||||
}
|
||||
|
||||
public JavaPairedBraceMatcher() {
|
||||
super(new JavaBraceMatcher(), JavaLanguage.INSTANCE, JavaFileType.INSTANCE, Holder.TYPE_TOKENS.toTokenSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IElementType lt() {
|
||||
return JavaTokenType.LT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull IElementType gt() {
|
||||
return JavaTokenType.GT;
|
||||
}
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.highlighting;
|
||||
|
||||
import com.intellij.lang.BracePair;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.PairedBraceMatcher;
|
||||
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.LanguageFileType;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class PairedBraceAndAnglesMatcher extends PairedBraceMatcherAdapter {
|
||||
private final TokenSet myTokenSetAllowedInsideAngleBrackets;
|
||||
private final LanguageFileType myFileType;
|
||||
|
||||
public PairedBraceAndAnglesMatcher(@NotNull PairedBraceMatcher matcher,
|
||||
@NotNull Language language,
|
||||
@NotNull LanguageFileType fileType,
|
||||
@NotNull TokenSet tokenSetAllowedInsideAngleBrackets) {
|
||||
super(matcher, language);
|
||||
myTokenSetAllowedInsideAngleBrackets = tokenSetAllowedInsideAngleBrackets;
|
||||
myFileType = fileType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLBraceToken(@NotNull HighlighterIterator iterator, @NotNull CharSequence fileText, @NotNull FileType fileType) {
|
||||
return isBrace(iterator, fileText, fileType, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRBraceToken(@NotNull HighlighterIterator iterator, @NotNull CharSequence fileText, @NotNull FileType fileType) {
|
||||
return isBrace(iterator, fileText, fileType, false);
|
||||
}
|
||||
|
||||
public abstract @NotNull IElementType lt();
|
||||
|
||||
public abstract @NotNull IElementType gt();
|
||||
|
||||
private boolean isBrace(HighlighterIterator iterator,
|
||||
CharSequence fileText,
|
||||
FileType fileType,
|
||||
boolean left) {
|
||||
final BracePair pair = findPair(left, iterator, fileText, fileType);
|
||||
if (pair == null) return false;
|
||||
|
||||
final IElementType opposite = left ? gt() : lt();
|
||||
if ((left ? pair.getRightBraceType() : pair.getLeftBraceType()) != opposite) return true;
|
||||
|
||||
if (fileType != myFileType) return false;
|
||||
|
||||
final IElementType braceElementType = left ? lt() : gt();
|
||||
int count = 0;
|
||||
try {
|
||||
int paired = 1;
|
||||
while (true) {
|
||||
count++;
|
||||
if (left) {
|
||||
iterator.advance();
|
||||
}
|
||||
else {
|
||||
iterator.retreat();
|
||||
}
|
||||
if (iterator.atEnd()) break;
|
||||
final IElementType tokenType = iterator.getTokenType();
|
||||
if (tokenType == opposite) {
|
||||
paired--;
|
||||
if (paired == 0) return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokenType == braceElementType) {
|
||||
paired++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!myTokenSetAllowedInsideAngleBrackets.contains(tokenType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
while (count-- > 0) {
|
||||
if (left) {
|
||||
iterator.retreat();
|
||||
}
|
||||
else {
|
||||
iterator.advance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2000-2020 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.ide.highlighter;
|
||||
|
||||
import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
|
||||
/**
|
||||
* Highlighting text attributes for Java language.
|
||||
*/
|
||||
public final class JavaHighlightingColors {
|
||||
public static final TextAttributesKey LINE_COMMENT = TextAttributesKey.createTextAttributesKey("JAVA_LINE_COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT);
|
||||
public static final TextAttributesKey JAVA_BLOCK_COMMENT = TextAttributesKey.createTextAttributesKey("JAVA_BLOCK_COMMENT", DefaultLanguageHighlighterColors.BLOCK_COMMENT);
|
||||
public static final TextAttributesKey DOC_COMMENT = TextAttributesKey.createTextAttributesKey("JAVA_DOC_COMMENT", DefaultLanguageHighlighterColors.DOC_COMMENT);
|
||||
public static final TextAttributesKey KEYWORD = TextAttributesKey.createTextAttributesKey("JAVA_KEYWORD", DefaultLanguageHighlighterColors.KEYWORD);
|
||||
public static final TextAttributesKey NUMBER = TextAttributesKey.createTextAttributesKey("JAVA_NUMBER", DefaultLanguageHighlighterColors.NUMBER);
|
||||
public static final TextAttributesKey STRING = TextAttributesKey.createTextAttributesKey("JAVA_STRING", DefaultLanguageHighlighterColors.STRING);
|
||||
public static final TextAttributesKey OPERATION_SIGN = TextAttributesKey.createTextAttributesKey("JAVA_OPERATION_SIGN", DefaultLanguageHighlighterColors.OPERATION_SIGN);
|
||||
public static final TextAttributesKey PARENTHESES = TextAttributesKey.createTextAttributesKey("JAVA_PARENTH", DefaultLanguageHighlighterColors.PARENTHESES);
|
||||
public static final TextAttributesKey BRACKETS = TextAttributesKey.createTextAttributesKey("JAVA_BRACKETS", DefaultLanguageHighlighterColors.BRACKETS);
|
||||
public static final TextAttributesKey BRACES = TextAttributesKey.createTextAttributesKey("JAVA_BRACES", DefaultLanguageHighlighterColors.BRACES);
|
||||
public static final TextAttributesKey COMMA = TextAttributesKey.createTextAttributesKey("JAVA_COMMA", DefaultLanguageHighlighterColors.COMMA);
|
||||
public static final TextAttributesKey DOT = TextAttributesKey.createTextAttributesKey("JAVA_DOT", DefaultLanguageHighlighterColors.DOT);
|
||||
public static final TextAttributesKey JAVA_SEMICOLON = TextAttributesKey.createTextAttributesKey("JAVA_SEMICOLON", DefaultLanguageHighlighterColors.SEMICOLON);
|
||||
public static final TextAttributesKey DOC_COMMENT_TAG = TextAttributesKey.createTextAttributesKey("JAVA_DOC_TAG", DefaultLanguageHighlighterColors.DOC_COMMENT_TAG);
|
||||
public static final TextAttributesKey DOC_COMMENT_MARKUP = TextAttributesKey.createTextAttributesKey("JAVA_DOC_MARKUP", DefaultLanguageHighlighterColors.DOC_COMMENT_MARKUP);
|
||||
public static final TextAttributesKey DOC_COMMENT_TAG_VALUE = TextAttributesKey.createTextAttributesKey("DOC_COMMENT_TAG_VALUE", DefaultLanguageHighlighterColors.DOC_COMMENT_TAG_VALUE);
|
||||
public static final TextAttributesKey VALID_STRING_ESCAPE = TextAttributesKey.createTextAttributesKey("JAVA_VALID_STRING_ESCAPE", DefaultLanguageHighlighterColors.VALID_STRING_ESCAPE);
|
||||
public static final TextAttributesKey INVALID_STRING_ESCAPE = TextAttributesKey.createTextAttributesKey("JAVA_INVALID_STRING_ESCAPE", DefaultLanguageHighlighterColors.INVALID_STRING_ESCAPE);
|
||||
public static final TextAttributesKey LOCAL_VARIABLE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("LOCAL_VARIABLE_ATTRIBUTES", DefaultLanguageHighlighterColors.LOCAL_VARIABLE);
|
||||
public static final TextAttributesKey PARAMETER_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("PARAMETER_ATTRIBUTES", DefaultLanguageHighlighterColors.PARAMETER);
|
||||
public static final TextAttributesKey LAMBDA_PARAMETER_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("LAMBDA_PARAMETER_ATTRIBUTES", PARAMETER_ATTRIBUTES);
|
||||
public static final TextAttributesKey REASSIGNED_LOCAL_VARIABLE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("REASSIGNED_LOCAL_VARIABLE_ATTRIBUTES", DefaultLanguageHighlighterColors.REASSIGNED_LOCAL_VARIABLE);
|
||||
public static final TextAttributesKey REASSIGNED_PARAMETER_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("REASSIGNED_PARAMETER_ATTRIBUTES", DefaultLanguageHighlighterColors.REASSIGNED_PARAMETER);
|
||||
public static final TextAttributesKey INSTANCE_FIELD_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("INSTANCE_FIELD_ATTRIBUTES", DefaultLanguageHighlighterColors.INSTANCE_FIELD);
|
||||
public static final TextAttributesKey INSTANCE_FINAL_FIELD_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("INSTANCE_FINAL_FIELD_ATTRIBUTES", INSTANCE_FIELD_ATTRIBUTES);
|
||||
public static final TextAttributesKey STATIC_FIELD_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("STATIC_FIELD_ATTRIBUTES", DefaultLanguageHighlighterColors.STATIC_FIELD);
|
||||
public static final TextAttributesKey STATIC_FIELD_IMPORTED_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("STATIC_FIELD_IMPORTED_ATTRIBUTES", STATIC_FIELD_ATTRIBUTES);
|
||||
public static final TextAttributesKey STATIC_FINAL_FIELD_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("STATIC_FINAL_FIELD_ATTRIBUTES", STATIC_FIELD_ATTRIBUTES);
|
||||
public static final TextAttributesKey STATIC_FINAL_FIELD_IMPORTED_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("STATIC_FINAL_FIELD_IMPORTED_ATTRIBUTES", STATIC_FINAL_FIELD_ATTRIBUTES);
|
||||
public static final TextAttributesKey CLASS_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("CLASS_NAME_ATTRIBUTES", DefaultLanguageHighlighterColors.CLASS_NAME);
|
||||
public static final TextAttributesKey ANONYMOUS_CLASS_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ANONYMOUS_CLASS_NAME_ATTRIBUTES", CLASS_NAME_ATTRIBUTES);
|
||||
public static final TextAttributesKey IMPLICIT_ANONYMOUS_CLASS_PARAMETER_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("IMPLICIT_ANONYMOUS_CLASS_PARAMETER_ATTRIBUTES", CLASS_NAME_ATTRIBUTES);
|
||||
public static final TextAttributesKey TYPE_PARAMETER_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("TYPE_PARAMETER_NAME_ATTRIBUTES", DefaultLanguageHighlighterColors.PARAMETER);
|
||||
public static final TextAttributesKey INTERFACE_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("INTERFACE_NAME_ATTRIBUTES", DefaultLanguageHighlighterColors.INTERFACE_NAME);
|
||||
public static final TextAttributesKey ENUM_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ENUM_NAME_ATTRIBUTES", CLASS_NAME_ATTRIBUTES);
|
||||
public static final TextAttributesKey ABSTRACT_CLASS_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ABSTRACT_CLASS_NAME_ATTRIBUTES", CLASS_NAME_ATTRIBUTES);
|
||||
public static final TextAttributesKey METHOD_CALL_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("METHOD_CALL_ATTRIBUTES", DefaultLanguageHighlighterColors.FUNCTION_CALL);
|
||||
public static final TextAttributesKey METHOD_DECLARATION_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("METHOD_DECLARATION_ATTRIBUTES", DefaultLanguageHighlighterColors.FUNCTION_DECLARATION);
|
||||
public static final TextAttributesKey STATIC_METHOD_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("STATIC_METHOD_ATTRIBUTES", DefaultLanguageHighlighterColors.STATIC_METHOD);
|
||||
public static final TextAttributesKey STATIC_METHOD_CALL_IMPORTED_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("STATIC_METHOD_IMPORTED_ATTRIBUTES", STATIC_METHOD_ATTRIBUTES);
|
||||
public static final TextAttributesKey ABSTRACT_METHOD_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ABSTRACT_METHOD_ATTRIBUTES", METHOD_CALL_ATTRIBUTES);
|
||||
public static final TextAttributesKey INHERITED_METHOD_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("INHERITED_METHOD_ATTRIBUTES", METHOD_CALL_ATTRIBUTES);
|
||||
public static final TextAttributesKey CONSTRUCTOR_CALL_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("CONSTRUCTOR_CALL_ATTRIBUTES", DefaultLanguageHighlighterColors.FUNCTION_CALL);
|
||||
public static final TextAttributesKey CONSTRUCTOR_DECLARATION_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("CONSTRUCTOR_DECLARATION_ATTRIBUTES", DefaultLanguageHighlighterColors.FUNCTION_DECLARATION);
|
||||
public static final TextAttributesKey ANNOTATION_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ANNOTATION_NAME_ATTRIBUTES", DefaultLanguageHighlighterColors.METADATA);
|
||||
public static final TextAttributesKey ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES", DefaultLanguageHighlighterColors.METADATA);
|
||||
public static final TextAttributesKey ANNOTATION_ATTRIBUTE_VALUE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("ANNOTATION_ATTRIBUTE_VALUE_ATTRIBUTES", DefaultLanguageHighlighterColors.METADATA);
|
||||
|
||||
//visibility
|
||||
public static final TextAttributesKey PUBLIC_REFERENCE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("PUBLIC_REFERENCE", (TextAttributesKey)null);
|
||||
public static final TextAttributesKey PROTECTED_REFERENCE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("PROTECTED_REFERENCE", (TextAttributesKey)null);
|
||||
public static final TextAttributesKey PACKAGE_PRIVATE_REFERENCE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("PACKAGE_PRIVATE_REFERENCE", (TextAttributesKey)null);
|
||||
public static final TextAttributesKey PRIVATE_REFERENCE_ATTRIBUTES = TextAttributesKey.createTextAttributesKey("PRIVATE_REFERENCE", (TextAttributesKey)null);
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.javadoc;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.CaretModel;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.LogicalPosition;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.BasicJavaAstTreeUtil;
|
||||
import com.intellij.psi.impl.source.BasicJavaTokenSet;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.impl.source.BasicJavaDocElementType.*;
|
||||
|
||||
public abstract class AbstractBasicJavadocHelper {
|
||||
private static final String PARAM_TEXT = "param";
|
||||
|
||||
public static final Pair<JavadocParameterInfo, List<JavadocParameterInfo>> EMPTY
|
||||
= new Pair<>(null, Collections.emptyList());
|
||||
private static final @NotNull BasicJavaTokenSet TAG_TOKEN_SET = BasicJavaTokenSet.create(DOC_TAG,
|
||||
DOC_SNIPPET_TAG,
|
||||
DOC_INLINE_TAG);
|
||||
|
||||
protected AbstractBasicJavadocHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to navigate caret at the given editor to the target position inserting missing white spaces if necessary.
|
||||
*
|
||||
* @param position target caret position
|
||||
* @param editor target editor
|
||||
* @param project target project
|
||||
*/
|
||||
public void navigate(@NotNull LogicalPosition position, @NotNull Editor editor, final @NotNull Project project) {
|
||||
final Document document = editor.getDocument();
|
||||
final CaretModel caretModel = editor.getCaretModel();
|
||||
final int endLineOffset = document.getLineEndOffset(position.line);
|
||||
final LogicalPosition endLinePosition = editor.offsetToLogicalPosition(endLineOffset);
|
||||
if (endLinePosition.column < position.column && !editor.getSettings().isVirtualSpace() && !editor.isViewer()) {
|
||||
final String toInsert = StringUtil.repeat(" ", position.column - endLinePosition.column);
|
||||
ApplicationManager.getApplication().runWriteAction(() -> {
|
||||
document.insertString(endLineOffset, toInsert);
|
||||
PsiDocumentManager.getInstance(project).commitDocument(document);
|
||||
});
|
||||
}
|
||||
caretModel.moveToLogicalPosition(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates desired position of target javadoc parameter's description start.
|
||||
*
|
||||
* @param psiFile PSI holder
|
||||
* @param data parsed adjacent javadoc parameters
|
||||
* @param anchor descriptor for the target parameter
|
||||
* @return logical position that points to the desired parameter description start location
|
||||
*/
|
||||
public @NotNull LogicalPosition calculateDescriptionStartPosition(@NotNull PsiFile psiFile,
|
||||
@NotNull Collection<? extends JavadocParameterInfo> data,
|
||||
@NotNull JavadocParameterInfo anchor) {
|
||||
int descriptionStartColumn = -1;
|
||||
int parameterNameEndColumn = -1;
|
||||
for (JavadocParameterInfo parameterInfo : data) {
|
||||
parameterNameEndColumn = Math.max(parameterNameEndColumn, parameterInfo.parameterNameEndPosition.column);
|
||||
if (parameterInfo.parameterDescriptionStartPosition != null) {
|
||||
descriptionStartColumn = Math.max(descriptionStartColumn, parameterInfo.parameterDescriptionStartPosition.column);
|
||||
}
|
||||
}
|
||||
|
||||
int column;
|
||||
|
||||
if (getJdAlignParamComments(psiFile)) {
|
||||
column = Math.max(descriptionStartColumn, parameterNameEndColumn);
|
||||
if (column <= parameterNameEndColumn) {
|
||||
column = parameterNameEndColumn + 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
column = anchor.parameterNameEndPosition.column + 1;
|
||||
}
|
||||
return new LogicalPosition(anchor.parameterNameEndPosition.line, column);
|
||||
}
|
||||
|
||||
protected abstract boolean getJdAlignParamComments(@NotNull PsiFile psiFile);
|
||||
|
||||
/**
|
||||
* Returns information about all lines that contain javadoc parameters and are adjacent to the one that holds given offset.
|
||||
*
|
||||
* @param psiFile PSI holder for the document exposed the given editor
|
||||
* @param editor target editor
|
||||
* @param offset target offset that identifies anchor line to check
|
||||
* @return pair like (javadoc info for the line identified by the given offset; list of javadoc parameter infos for
|
||||
* adjacent lines if any
|
||||
*/
|
||||
public @NotNull Pair<JavadocParameterInfo, List<JavadocParameterInfo>> parse(@NotNull PsiFile psiFile,
|
||||
@NotNull Editor editor,
|
||||
int offset) {
|
||||
List<JavadocParameterInfo> result = new ArrayList<>();
|
||||
PsiDocumentManager.getInstance(psiFile.getProject()).commitDocument(editor.getDocument());
|
||||
final PsiElement elementAtCaret = psiFile.findElementAt(offset);
|
||||
if (elementAtCaret == null) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
ASTNode nodeAtCaret = BasicJavaAstTreeUtil.toNode(elementAtCaret);
|
||||
ASTNode tag = BasicJavaAstTreeUtil.getParentOfType(nodeAtCaret, TAG_TOKEN_SET);
|
||||
if (tag == null) {
|
||||
// Due to javadoc PSI specifics.
|
||||
if (BasicJavaAstTreeUtil.isWhiteSpace(nodeAtCaret)) {
|
||||
for (ASTNode e = nodeAtCaret.getTreePrev(); e != null && tag == null; e = e.getTreePrev()) {
|
||||
tag = BasicJavaAstTreeUtil.getParentOfType(e, TAG_TOKEN_SET, false);
|
||||
if (e instanceof PsiWhiteSpace
|
||||
|| (e.getElementType() == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tag == null) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
JavadocParameterInfo anchorInfo = parse(tag, editor);
|
||||
if (anchorInfo == null) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
// Parse previous parameters.
|
||||
for (ASTNode n = tag.getTreePrev(); n != null; n = n.getTreePrev()) {
|
||||
JavadocParameterInfo info = parse(n, editor);
|
||||
if (info == null) {
|
||||
break;
|
||||
}
|
||||
result.add(0, info);
|
||||
}
|
||||
|
||||
result.add(anchorInfo);
|
||||
|
||||
// Parse subsequent parameters.
|
||||
for (ASTNode n = tag.getTreeNext(); n != null; n = n.getTreeNext()) {
|
||||
JavadocParameterInfo info = parse(n, editor);
|
||||
if (info == null) {
|
||||
break;
|
||||
}
|
||||
result.add(info);
|
||||
}
|
||||
|
||||
return Pair.create(anchorInfo, result);
|
||||
}
|
||||
|
||||
private static @Nullable JavadocParameterInfo parse(@NotNull ASTNode astNode, @NotNull Editor editor) {
|
||||
final ASTNode tag = BasicJavaAstTreeUtil.getParentOfType(astNode, TAG_TOKEN_SET, false);
|
||||
if (tag == null || !PARAM_TEXT.equals(BasicJavaAstTreeUtil.getTagName(tag))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final ASTNode paramRef = BasicJavaAstTreeUtil.findChildByType(tag, DOC_TAG_VALUE_ELEMENT,
|
||||
DOC_METHOD_OR_FIELD_REF,
|
||||
DOC_PARAMETER_REF,
|
||||
DOC_SNIPPET_TAG_VALUE);
|
||||
if (paramRef == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (ASTNode node = paramRef.getTreeNext(); node != null; node = node.getTreeNext()) {
|
||||
final IElementType elementType = node.getElementType();
|
||||
if (elementType == JavaDocTokenType.DOC_COMMENT_DATA) {
|
||||
return new JavadocParameterInfo(
|
||||
editor.offsetToLogicalPosition(paramRef.getTextRange().getEndOffset()),
|
||||
editor.offsetToLogicalPosition(node.getTextRange().getStartOffset()),
|
||||
editor.getDocument().getLineNumber(node.getTextRange().getEndOffset())
|
||||
);
|
||||
}
|
||||
else if (elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return new JavadocParameterInfo(
|
||||
editor.offsetToLogicalPosition(paramRef.getTextRange().getEndOffset()),
|
||||
null,
|
||||
editor.getDocument().getLineNumber(paramRef.getTextRange().getEndOffset())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates information about source code line that holds javadoc parameter.
|
||||
*/
|
||||
public static class JavadocParameterInfo {
|
||||
|
||||
/**
|
||||
* Logical position that points to location just after javadoc parameter name.
|
||||
* <p/>
|
||||
* Example:
|
||||
* <pre>
|
||||
* /**
|
||||
* * @param i[X] description
|
||||
* */
|
||||
* </pre>
|
||||
*/
|
||||
public final @NotNull LogicalPosition parameterNameEndPosition;
|
||||
public final @Nullable LogicalPosition parameterDescriptionStartPosition;
|
||||
/** Last logical line occupied by the current javadoc parameter. */
|
||||
public final int lastLine;
|
||||
|
||||
public JavadocParameterInfo(@NotNull LogicalPosition parameterNameEndPosition,
|
||||
@Nullable LogicalPosition parameterDescriptionStartPosition,
|
||||
int lastLine) {
|
||||
this.parameterNameEndPosition = parameterNameEndPosition;
|
||||
this.parameterDescriptionStartPosition = parameterDescriptionStartPosition;
|
||||
this.lastLine = lastLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "name end: " + parameterNameEndPosition + ", description start: " + parameterDescriptionStartPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
+199
@@ -0,0 +1,199 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.openapi.options.colors.pages;
|
||||
|
||||
import com.intellij.application.options.colors.InspectionColorSettingsPage;
|
||||
import com.intellij.codeHighlighting.RainbowHighlighter;
|
||||
import com.intellij.core.JavaOptionBundle;
|
||||
import com.intellij.ide.highlighter.JavaHighlightingColors;
|
||||
import com.intellij.openapi.editor.colors.CodeInsightColors;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.options.OptionsBundle;
|
||||
import com.intellij.openapi.options.colors.AttributesDescriptor;
|
||||
import com.intellij.openapi.options.colors.ColorDescriptor;
|
||||
import com.intellij.openapi.options.colors.ColorSettingsPage;
|
||||
import com.intellij.psi.codeStyle.DisplayPriority;
|
||||
import com.intellij.psi.codeStyle.DisplayPrioritySortable;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractBasicJavaColorSettingsPage
|
||||
implements ColorSettingsPage, InspectionColorSettingsPage, DisplayPrioritySortable {
|
||||
private static final AttributesDescriptor[] ourDescriptors = {
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.keyword"), JavaHighlightingColors.KEYWORD),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.number"), JavaHighlightingColors.NUMBER),
|
||||
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.string"), JavaHighlightingColors.STRING),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.valid.escape.in.string"), JavaHighlightingColors.VALID_STRING_ESCAPE),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.invalid.escape.in.string"), JavaHighlightingColors.INVALID_STRING_ESCAPE),
|
||||
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.operator.sign"), JavaHighlightingColors.OPERATION_SIGN),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.parentheses"), JavaHighlightingColors.PARENTHESES),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.braces"), JavaHighlightingColors.BRACES),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.brackets"), JavaHighlightingColors.BRACKETS),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.comma"), JavaHighlightingColors.COMMA),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.semicolon"), JavaHighlightingColors.JAVA_SEMICOLON),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.dot"), JavaHighlightingColors.DOT),
|
||||
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.line.comment"), JavaHighlightingColors.LINE_COMMENT),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.block.comment"), JavaHighlightingColors.JAVA_BLOCK_COMMENT),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.javadoc.comment"), JavaHighlightingColors.DOC_COMMENT),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.javadoc.tag"), JavaHighlightingColors.DOC_COMMENT_TAG),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.javadoc.tag.value"), JavaHighlightingColors.DOC_COMMENT_TAG_VALUE),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.javadoc.markup"), JavaHighlightingColors.DOC_COMMENT_MARKUP),
|
||||
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.class"), JavaHighlightingColors.CLASS_NAME_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.anonymous.class"), JavaHighlightingColors.ANONYMOUS_CLASS_NAME_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.type.parameter"), JavaHighlightingColors.TYPE_PARAMETER_NAME_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.abstract.class"), JavaHighlightingColors.ABSTRACT_CLASS_NAME_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.interface"), JavaHighlightingColors.INTERFACE_NAME_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.enum"), JavaHighlightingColors.ENUM_NAME_ATTRIBUTES),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.local.variable"), JavaHighlightingColors.LOCAL_VARIABLE_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.reassigned.local.variable"), JavaHighlightingColors.REASSIGNED_LOCAL_VARIABLE_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.reassigned.parameter"), JavaHighlightingColors.REASSIGNED_PARAMETER_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.implicit.anonymous.parameter"), JavaHighlightingColors.IMPLICIT_ANONYMOUS_CLASS_PARAMETER_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.instance.field"), JavaHighlightingColors.INSTANCE_FIELD_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.instance.final.field"), JavaHighlightingColors.INSTANCE_FINAL_FIELD_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.field"), JavaHighlightingColors.STATIC_FIELD_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.imported.field"), JavaHighlightingColors.STATIC_FIELD_IMPORTED_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.final.field"), JavaHighlightingColors.STATIC_FINAL_FIELD_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.final.imported.field"), JavaHighlightingColors.STATIC_FINAL_FIELD_IMPORTED_ATTRIBUTES),
|
||||
new AttributesDescriptor(OptionsBundle.message("options.java.attribute.descriptor.parameter"), JavaHighlightingColors.PARAMETER_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.lambda.parameter"), JavaHighlightingColors.LAMBDA_PARAMETER_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.method.call"), JavaHighlightingColors.METHOD_CALL_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.method.imported.call"), JavaHighlightingColors.STATIC_METHOD_CALL_IMPORTED_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.method.declaration"), JavaHighlightingColors.METHOD_DECLARATION_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.constructor.call"), JavaHighlightingColors.CONSTRUCTOR_CALL_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.constructor.declaration"), JavaHighlightingColors.CONSTRUCTOR_DECLARATION_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.static.method"), JavaHighlightingColors.STATIC_METHOD_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.abstract.method"), JavaHighlightingColors.ABSTRACT_METHOD_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.inherited.method"), JavaHighlightingColors.INHERITED_METHOD_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.public"), JavaHighlightingColors.PUBLIC_REFERENCE_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.protected"), JavaHighlightingColors.PROTECTED_REFERENCE_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.package.private"), JavaHighlightingColors.PACKAGE_PRIVATE_REFERENCE_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.private"), JavaHighlightingColors.PRIVATE_REFERENCE_ATTRIBUTES),
|
||||
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.annotation.name"), JavaHighlightingColors.ANNOTATION_NAME_ATTRIBUTES),
|
||||
new AttributesDescriptor(JavaOptionBundle.message("options.java.attribute.descriptor.annotation.attribute.name"), JavaHighlightingColors.ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES)
|
||||
};
|
||||
|
||||
@NonNls private static final Map<String, TextAttributesKey> ourTags = RainbowHighlighter.createRainbowHLM();
|
||||
static {
|
||||
ourTags.put("field", JavaHighlightingColors.INSTANCE_FIELD_ATTRIBUTES);
|
||||
ourTags.put("unusedField", CodeInsightColors.NOT_USED_ELEMENT_ATTRIBUTES);
|
||||
ourTags.put("error", CodeInsightColors.ERRORS_ATTRIBUTES);
|
||||
ourTags.put("warning", CodeInsightColors.WARNINGS_ATTRIBUTES);
|
||||
ourTags.put("weak_warning", CodeInsightColors.WEAK_WARNING_ATTRIBUTES);
|
||||
ourTags.put("server_problems", CodeInsightColors.GENERIC_SERVER_ERROR_OR_WARNING);
|
||||
ourTags.put("server_duplicate", CodeInsightColors.DUPLICATE_FROM_SERVER);
|
||||
ourTags.put("unknownType", CodeInsightColors.WRONG_REFERENCES_ATTRIBUTES);
|
||||
ourTags.put("localVar", JavaHighlightingColors.LOCAL_VARIABLE_ATTRIBUTES);
|
||||
ourTags.put("reassignedLocalVar", JavaHighlightingColors.REASSIGNED_LOCAL_VARIABLE_ATTRIBUTES);
|
||||
ourTags.put("implicitAnonymousParameter", JavaHighlightingColors.IMPLICIT_ANONYMOUS_CLASS_PARAMETER_ATTRIBUTES);
|
||||
ourTags.put("static", JavaHighlightingColors.STATIC_FIELD_ATTRIBUTES);
|
||||
ourTags.put("static_final", JavaHighlightingColors.STATIC_FINAL_FIELD_ATTRIBUTES);
|
||||
ourTags.put("deprecated", CodeInsightColors.DEPRECATED_ATTRIBUTES);
|
||||
ourTags.put("for_removal", CodeInsightColors.MARKED_FOR_REMOVAL_ATTRIBUTES);
|
||||
ourTags.put("constructorCall", JavaHighlightingColors.CONSTRUCTOR_CALL_ATTRIBUTES);
|
||||
ourTags.put("constructorDeclaration", JavaHighlightingColors.CONSTRUCTOR_DECLARATION_ATTRIBUTES);
|
||||
ourTags.put("methodCall", JavaHighlightingColors.METHOD_CALL_ATTRIBUTES);
|
||||
ourTags.put("methodDeclaration", JavaHighlightingColors.METHOD_DECLARATION_ATTRIBUTES);
|
||||
ourTags.put("static_method", JavaHighlightingColors.STATIC_METHOD_ATTRIBUTES);
|
||||
ourTags.put("abstract_method", JavaHighlightingColors.ABSTRACT_METHOD_ATTRIBUTES);
|
||||
ourTags.put("inherited_method", JavaHighlightingColors.INHERITED_METHOD_ATTRIBUTES);
|
||||
ourTags.put("param", JavaHighlightingColors.PARAMETER_ATTRIBUTES);
|
||||
ourTags.put("lambda_param", JavaHighlightingColors.LAMBDA_PARAMETER_ATTRIBUTES);
|
||||
ourTags.put("class", JavaHighlightingColors.CLASS_NAME_ATTRIBUTES);
|
||||
ourTags.put("anonymousClass", JavaHighlightingColors.ANONYMOUS_CLASS_NAME_ATTRIBUTES);
|
||||
ourTags.put("typeParameter", JavaHighlightingColors.TYPE_PARAMETER_NAME_ATTRIBUTES);
|
||||
ourTags.put("abstractClass", JavaHighlightingColors.ABSTRACT_CLASS_NAME_ATTRIBUTES);
|
||||
ourTags.put("interface", JavaHighlightingColors.INTERFACE_NAME_ATTRIBUTES);
|
||||
ourTags.put("enum", JavaHighlightingColors.ENUM_NAME_ATTRIBUTES);
|
||||
ourTags.put("annotationName", JavaHighlightingColors.ANNOTATION_NAME_ATTRIBUTES);
|
||||
ourTags.put("annotationAttributeName", JavaHighlightingColors.ANNOTATION_ATTRIBUTE_NAME_ATTRIBUTES);
|
||||
ourTags.put("javadocTagValue", JavaHighlightingColors.DOC_COMMENT_TAG_VALUE);
|
||||
ourTags.put("instanceFinalField", JavaHighlightingColors.INSTANCE_FINAL_FIELD_ATTRIBUTES);
|
||||
ourTags.put("staticallyConstImported", JavaHighlightingColors.STATIC_FINAL_FIELD_IMPORTED_ATTRIBUTES);
|
||||
ourTags.put("staticallyImported", JavaHighlightingColors.STATIC_FIELD_IMPORTED_ATTRIBUTES);
|
||||
ourTags.put("static_imported_method", JavaHighlightingColors.STATIC_METHOD_CALL_IMPORTED_ATTRIBUTES);
|
||||
ourTags.put("public", JavaHighlightingColors.PUBLIC_REFERENCE_ATTRIBUTES);
|
||||
ourTags.put("protected", JavaHighlightingColors.PROTECTED_REFERENCE_ATTRIBUTES);
|
||||
ourTags.put("package_private", JavaHighlightingColors.PACKAGE_PRIVATE_REFERENCE_ATTRIBUTES);
|
||||
ourTags.put("private", JavaHighlightingColors.PRIVATE_REFERENCE_ATTRIBUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDisplayName() {
|
||||
return JavaOptionBundle.message("options.java.display.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Icon getIcon();
|
||||
|
||||
@Override
|
||||
public AttributesDescriptor @NotNull [] getAttributeDescriptors() {
|
||||
return ourDescriptors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorDescriptor @NotNull [] getColorDescriptors() {
|
||||
return ColorDescriptor.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public String getDemoText() {
|
||||
return
|
||||
"""
|
||||
/* Block comment */
|
||||
import <class>java.util.Date</class>;
|
||||
/**
|
||||
* Doc comment here for <code>SomeClass</code>
|
||||
* @param <javadocTagValue>T</javadocTagValue> type parameter
|
||||
* @see <class>Math</class>#<methodCall>sin</methodCall>(double)
|
||||
*/
|
||||
<annotationName>@Annotation</annotationName> (<annotationAttributeName>name</annotationAttributeName>=value)
|
||||
public class <class>SomeClass</class><<typeParameter>T</typeParameter> extends <interface>Runnable</interface>> { // some comment
|
||||
private <typeParameter>T</typeParameter> <field>field</field> = null;
|
||||
private double <unusedField>unusedField</unusedField> = 12345.67890;
|
||||
private <unknownType>UnknownType</unknownType> <field>anotherString</field> = "Another\\nStrin\\g";
|
||||
public static int <static>staticField</static> = 0;
|
||||
public final int <instanceFinalField>instanceFinalField</instanceFinalField> = 0;
|
||||
|
||||
public <constructorDeclaration>SomeClass</constructorDeclaration>(<interface>AnInterface</interface> <param>param</param>, int[] <reassignedParameter>reassignedParam</reassignedParameter>) {
|
||||
<error>int <localVar>localVar</localVar> = "IntelliJ"</error>; // Error, incompatible types
|
||||
<class>System</class>.<static>out</static>.<methodCall>println</methodCall>(<field>anotherString</field> + <inherited_method>toString</inherited_method>() + <localVar>localVar</localVar>);
|
||||
long <localVar>time</localVar> = <class>Date</class>.<static_method><deprecated>parse</deprecated></static_method>("1.2.3"); // Method is deprecated
|
||||
int <reassignedLocalVar>reassignedValue</reassignedLocalVar> = this.<warning>staticField</warning>;\s
|
||||
<reassignedLocalVar>reassignedValue</reassignedLocalVar> ++;\s
|
||||
<field>field</field>.<abstract_method>run</abstract_method>();\s
|
||||
new <anonymousClass>SomeClass</anonymousClass>() {
|
||||
{
|
||||
int <localVar>a</localVar> = <implicitAnonymousParameter>localVar</implicitAnonymousParameter>;
|
||||
}
|
||||
};
|
||||
<reassignedParameter>reassignedParam</reassignedParameter> = new <constructorCall>ArrayList</constructorCall><<class>String</class>>().toArray(new int[0]);
|
||||
}
|
||||
}
|
||||
enum <enum>AnEnum</enum> { <static_final>CONST1</static_final>, <static_final>CONST2</static_final> }
|
||||
interface <interface>AnInterface</interface> {
|
||||
int <static_final>CONSTANT</static_final> = 2;
|
||||
void <methodDeclaration>method</methodDeclaration>();
|
||||
}
|
||||
abstract class <abstractClass>SomeAbstractClass</abstractClass> {
|
||||
}""";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
|
||||
return ourTags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DisplayPriority getPriority() {
|
||||
return DisplayPriority.KEY_LANGUAGE_SETTINGS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user