com.intellij.json.editor.lineMover.JsonLineMover Maven / Gradle / Ivy
package com.intellij.json.editor.lineMover;
import com.intellij.codeInsight.editorActions.moveUpDown.LineMover;
import com.intellij.codeInsight.editorActions.moveUpDown.LineRange;
import com.intellij.json.JsonElementTypes;
import com.intellij.json.psi.*;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Mikhail Golubev
*/
public class JsonLineMover extends LineMover {
private boolean myShouldAddComma = false;
@Override
public boolean checkAvailable(@NotNull Editor editor, @NotNull PsiFile file, @NotNull MoveInfo info, boolean down) {
myShouldAddComma = false;
if (!(file instanceof JsonFile) || !super.checkAvailable(editor, file, info, down)) {
return false;
}
Pair movedElementRange = getElementRange(editor, file, info.toMove);
if (!isValidElementRange(movedElementRange)) {
return false;
}
// Tweak range to move if it's necessary
movedElementRange = expandCommentsInRange(movedElementRange);
info.toMove = new LineRange(movedElementRange.getFirst(), movedElementRange.getSecond());
// Adjust destination range to prevent illegal offsets
final int lineCount = editor.getDocument().getLineCount();
if (down) {
info.toMove2 = new LineRange(info.toMove.endLine, Math.min(info.toMove.endLine + 1, lineCount));
}
else {
info.toMove2 = new LineRange(Math.max(info.toMove.startLine - 1, 0), info.toMove.startLine);
}
if (movedElementRange.getFirst() instanceof PsiComment && movedElementRange.getSecond() instanceof PsiComment) {
return true;
}
// Check whether additional comma is needed
final Pair destElementRange = getElementRange(editor, file, info.toMove2);
if (isValidElementRange(destElementRange) && movedElementRange.getFirst().getParent() == destElementRange.getSecond().getParent()) {
final PsiElement commonParent = movedElementRange.getFirst().getParent();
final PsiElement lowerRightElement = down ? destElementRange.getSecond() : movedElementRange.getSecond();
// Destination rightmost element is not closing brace or bracket
if (lowerRightElement instanceof JsonElement) {
if (commonParent instanceof JsonArray && notFollowedByNextElementOrComma(lowerRightElement, JsonValue.class) ||
commonParent instanceof JsonObject && notFollowedByNextElementOrComma(lowerRightElement, JsonProperty.class)) {
myShouldAddComma = true;
}
}
}
return true;
}
@NotNull
private Pair expandCommentsInRange(@NotNull Pair range) {
final PsiElement upper = JsonPsiUtil.findFurthestSiblingOfSameType(range.getFirst(), false);
final PsiElement lower = JsonPsiUtil.findFurthestSiblingOfSameType(range.getSecond(), true);
return Pair.create(upper, lower);
}
@Override
public void beforeMove(@NotNull Editor editor, @NotNull MoveInfo info, boolean down) {
if (myShouldAddComma) {
final Document document = editor.getDocument();
final int lineBelow = down ? info.toMove2.endLine - 1 : info.toMove.endLine - 1;
document.insertString(document.getLineEndOffset(lineBelow), ",");
final int lineAbove = down ? info.toMove.endLine - 1 : info.toMove2.endLine - 1;
final int lineAboveEndOffset = document.getLineEndOffset(lineAbove);
final String aboveLineEnding = document.getText(new TextRange(lineAboveEndOffset - 1, lineAboveEndOffset));
if (aboveLineEnding.equals(",")) {
document.deleteString(lineAboveEndOffset - 1, lineAboveEndOffset);
}
final Project project = editor.getProject();
assert project != null;
PsiDocumentManager.getInstance(project).commitDocument(document);
}
}
private boolean notFollowedByNextElementOrComma(@NotNull PsiElement anchor, @NotNull Class extends PsiElement> nextElementType) {
return PsiTreeUtil.getNextSiblingOfType(anchor, nextElementType) == null &&
TreeUtil.findSibling(anchor.getNode(), JsonElementTypes.COMMA) == null;
}
private boolean isValidElementRange(@Nullable Pair elementRange) {
if (elementRange == null) {
return false;
}
return elementRange.getFirst().getParent() == elementRange.getSecond().getParent();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy