Beispiel #1
0
  private static BraceStatus checkForMovableDownClosingBrace(
      @NotNull PsiElement closingBrace,
      @NotNull PsiElement block,
      @NotNull Editor editor,
      @NotNull MoveInfo info) {
    PsiElement current = block;
    PsiElement nextElement = null;
    PsiElement nextExpression = null;
    do {
      PsiElement sibling = firstNonWhiteElement(current.getNextSibling(), true);
      if (sibling != null && nextElement == null) {
        nextElement = sibling;
      }

      if (sibling instanceof JetExpression) {
        nextExpression = sibling;
        break;
      }

      current = current.getParent();
    } while (current != null && !(PsiTreeUtil.instanceOf(current, BLOCKLIKE_ELEMENT_CLASSES)));

    if (nextExpression == null) return BraceStatus.NOT_MOVABLE;

    Document doc = editor.getDocument();

    info.toMove = new LineRange(closingBrace, closingBrace, doc);
    info.toMove2 = new LineRange(nextElement, nextExpression);
    info.indentSource = true;

    return BraceStatus.MOVABLE;
  }
  @Override
  public boolean checkAvailable(
      @NotNull final Editor editor,
      @NotNull final PsiFile file,
      @NotNull final MoveInfo info,
      final boolean down) {
    // if (!(file instanceof PsiJavaFile)) return false;
    final boolean available = super.checkAvailable(editor, file, info, down);
    if (!available) return false;
    LineRange range = info.toMove;

    range = expandLineRangeToCoverPsiElements(range, editor, file);
    if (range == null) return false;
    info.toMove = range;
    final int startOffset = editor.logicalPositionToOffset(new LogicalPosition(range.startLine, 0));
    final int endOffset = editor.logicalPositionToOffset(new LogicalPosition(range.endLine, 0));
    final PsiElement[] statements =
        CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset);
    if (statements.length == 0) return false;
    range.firstElement = statements[0];
    range.lastElement = statements[statements.length - 1];

    if (!checkMovingInsideOutside(file, editor, range, info, down)) {
      info.toMove2 = null;
      return true;
    }
    return true;
  }
  private boolean checkMovingInsideOutside(
      PsiFile file,
      final Editor editor,
      LineRange range,
      @NotNull final MoveInfo info,
      final boolean down) {
    final int offset = editor.getCaretModel().getOffset();

    PsiElement elementAtOffset = file.getViewProvider().findElementAt(offset, StdLanguages.JAVA);
    if (elementAtOffset == null) return false;

    PsiElement guard = elementAtOffset;
    do {
      guard =
          PsiTreeUtil.getParentOfType(
              guard, PsiMethod.class, PsiClassInitializer.class, PsiClass.class, PsiComment.class);
    } while (guard instanceof PsiAnonymousClass);

    PsiElement brace = itIsTheClosingCurlyBraceWeAreMoving(file, editor);
    if (brace != null) {
      int line = editor.getDocument().getLineNumber(offset);
      final LineRange toMove = new LineRange(line, line + 1);
      toMove.firstElement = toMove.lastElement = brace;
      info.toMove = toMove;
    }

    // cannot move in/outside method/class/initializer/comment
    if (!calcInsertOffset(file, editor, info.toMove, info, down)) return false;
    int insertOffset =
        down
            ? getLineStartSafeOffset(editor.getDocument(), info.toMove2.endLine)
            : editor.getDocument().getLineStartOffset(info.toMove2.startLine);
    PsiElement elementAtInsertOffset =
        file.getViewProvider().findElementAt(insertOffset, StdLanguages.JAVA);
    PsiElement newGuard = elementAtInsertOffset;
    do {
      newGuard =
          PsiTreeUtil.getParentOfType(
              newGuard,
              PsiMethod.class,
              PsiClassInitializer.class,
              PsiClass.class,
              PsiComment.class);
    } while (newGuard instanceof PsiAnonymousClass);

    if (brace != null
        && PsiTreeUtil.getParentOfType(brace, PsiCodeBlock.class, false)
            != PsiTreeUtil.getParentOfType(elementAtInsertOffset, PsiCodeBlock.class, false)) {
      info.indentSource = true;
    }
    if (newGuard == guard && isInside(insertOffset, newGuard) == isInside(offset, guard))
      return true;

    // moving in/out nested class is OK
    if (guard instanceof PsiClass && guard.getParent() instanceof PsiClass) return true;
    if (newGuard instanceof PsiClass && newGuard.getParent() instanceof PsiClass) return true;

    return false;
  }
Beispiel #4
0
  @Override
  public boolean checkAvailable(
      @NotNull Editor editor, @NotNull PsiFile file, @NotNull MoveInfo info, boolean down) {
    parametersOrArgsToMove = null;

    if (!super.checkAvailable(editor, file, info, down)) return false;

    switch (checkForMovableClosingBrace(editor, file, info, down)) {
      case NOT_MOVABLE:
        {
          info.toMove2 = null;
          return true;
        }
      case MOVABLE:
        return true;
      default:
        break;
    }

    LineRange oldRange = info.toMove;

    Pair<PsiElement, PsiElement> psiRange = getElementRange(editor, file, oldRange);
    if (psiRange == null) return false;

    //noinspection unchecked
    PsiElement firstElement = getMovableElement(psiRange.getFirst(), false);
    PsiElement lastElement = getMovableElement(psiRange.getSecond(), true);

    if (firstElement == null || lastElement == null) return false;

    if (isForbiddenMove(firstElement, down) || isForbiddenMove(lastElement, down)) {
      info.toMove2 = null;
      return true;
    }

    if ((firstElement instanceof JetParameter || firstElement instanceof JetValueArgument)
        && PsiTreeUtil.isAncestor(lastElement, firstElement, false)) {
      lastElement = firstElement;
    }

    LineRange sourceRange = getSourceRange(firstElement, lastElement, editor, oldRange);
    if (sourceRange == null) return false;

    PsiElement sibling =
        getLastNonWhiteSiblingInLine(adjustSibling(editor, sourceRange, info, down), editor, down);

    // Either reached last sibling, or jumped over multi-line whitespace
    if (sibling == null) return true;

    info.toMove = sourceRange;
    info.toMove2 = getTargetRange(editor, sourceRange.firstElement, sibling, down);
    return true;
  }
  private boolean calcInsertOffset(
      PsiFile file,
      final Editor editor,
      LineRange range,
      @NotNull final MoveInfo info,
      final boolean down) {
    int line = down ? range.endLine + 1 : range.startLine - 1;
    int startLine = down ? range.endLine : range.startLine - 1;
    if (line < 0 || startLine < 0) return false;
    while (true) {
      final int offset = editor.logicalPositionToOffset(new LogicalPosition(line, 0));
      PsiElement element = firstNonWhiteElement(offset, file, true);

      while (element != null && !(element instanceof PsiFile)) {
        if (!element.getTextRange().grown(-1).shiftRight(1).contains(offset)) {
          PsiElement elementToSurround = null;
          boolean found = false;
          if ((element instanceof PsiStatement || element instanceof PsiComment)
              && statementCanBePlacedAlong(element)) {
            found = true;
            if (!(element.getParent() instanceof PsiCodeBlock)) {
              elementToSurround = element;
            }
          } else if (element instanceof PsiJavaToken
              && ((PsiJavaToken) element).getTokenType() == JavaTokenType.RBRACE
              && element.getParent() instanceof PsiCodeBlock) {
            // before code block closing brace
            found = true;
          }
          if (found) {
            statementToSurroundWithCodeBlock = elementToSurround;
            info.toMove = range;
            int endLine = line;
            if (startLine > endLine) {
              int tmp = endLine;
              endLine = startLine;
              startLine = tmp;
            }

            info.toMove2 =
                down ? new LineRange(startLine, endLine) : new LineRange(startLine, endLine + 1);
            return true;
          }
        }
        element = element.getParent();
      }
      line += down ? 1 : -1;
      if (line == 0 || line >= editor.getDocument().getLineCount()) {
        return false;
      }
    }
  }
Beispiel #6
0
  protected static PsiElement adjustSibling(
      @NotNull Editor editor,
      @NotNull LineRange sourceRange,
      @NotNull MoveInfo info,
      boolean down) {
    PsiElement element = down ? sourceRange.lastElement : sourceRange.firstElement;
    PsiElement sibling = down ? element.getNextSibling() : element.getPrevSibling();

    PsiElement whiteSpaceTestSubject = sibling;
    if (sibling == null) {
      PsiElement parent = element.getParent();
      if (parent != null && isBracelessBlock(parent)) {
        whiteSpaceTestSubject = down ? parent.getNextSibling() : parent.getPrevSibling();
      }
    }

    if (whiteSpaceTestSubject instanceof PsiWhiteSpace) {
      if (getElementLineCount(whiteSpaceTestSubject, editor) > 1) {
        int nearLine = down ? sourceRange.endLine : sourceRange.startLine - 1;

        info.toMove = sourceRange;
        info.toMove2 = new LineRange(nearLine, nearLine + 1);
        info.indentTarget = false;

        return null;
      }

      if (sibling != null) {
        sibling = firstNonWhiteElement(sibling, down);
      }
    }

    if (sibling == null) {
      JetCallExpression callExpression =
          PsiTreeUtil.getParentOfType(element, JetCallExpression.class);
      if (callExpression != null) {
        JetBlockExpression dslBlock = getDSLLambdaBlock(callExpression, down);
        if (PsiTreeUtil.isAncestor(dslBlock, element, false)) {
          //noinspection ConstantConditions
          PsiElement blockParent = dslBlock.getParent();
          return down
              ? JetPsiUtil.findChildByType(blockParent, JetTokens.RBRACE)
              : JetPsiUtil.findChildByType(blockParent, JetTokens.LBRACE);
        }
      }

      info.toMove2 = null;
      return null;
    }

    return sibling;
  }
Beispiel #7
0
  public boolean checkAvailable(
      @NotNull final Editor editor,
      @NotNull final PsiFile file,
      @NotNull final MoveInfo info,
      final boolean down) {
    LineRange range = StatementUpDownMover.getLineRangeFromSelection(editor);

    final int maxLine = editor.offsetToLogicalPosition(editor.getDocument().getTextLength()).line;
    if (range.startLine == 0 && !down) return false;
    if (range.endLine >= maxLine && down) return false;

    int nearLine = down ? range.endLine : range.startLine - 1;
    info.toMove = range;
    info.toMove2 = new LineRange(nearLine, nearLine + 1);

    return true;
  }
  private void surroundWithCodeBlock(@NotNull final MoveInfo info, final boolean down) {
    try {
      final Document document =
          PsiDocumentManager.getInstance(statementToSurroundWithCodeBlock.getProject())
              .getDocument(statementToSurroundWithCodeBlock.getContainingFile());
      int startOffset = document.getLineStartOffset(info.toMove.startLine);
      int endOffset = getLineStartSafeOffset(document, info.toMove.endLine);
      if (document.getText().charAt(endOffset - 1) == '\n') endOffset--;
      final RangeMarker lineRangeMarker = document.createRangeMarker(startOffset, endOffset);

      final PsiElementFactory factory =
          JavaPsiFacade.getInstance(statementToSurroundWithCodeBlock.getProject())
              .getElementFactory();
      PsiCodeBlock codeBlock = factory.createCodeBlock();
      codeBlock.add(statementToSurroundWithCodeBlock);
      final PsiBlockStatement blockStatement =
          (PsiBlockStatement)
              factory.createStatementFromText("{}", statementToSurroundWithCodeBlock);
      blockStatement.getCodeBlock().replace(codeBlock);
      PsiBlockStatement newStatement =
          (PsiBlockStatement) statementToSurroundWithCodeBlock.replace(blockStatement);
      newStatement = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newStatement);
      info.toMove =
          new LineRange(
              document.getLineNumber(lineRangeMarker.getStartOffset()),
              document.getLineNumber(lineRangeMarker.getEndOffset()) + 1);
      PsiCodeBlock newCodeBlock = newStatement.getCodeBlock();
      if (down) {
        PsiElement blockChild = firstNonWhiteElement(newCodeBlock.getFirstBodyElement(), true);
        if (blockChild == null) blockChild = newCodeBlock.getRBrace();
        info.toMove2 =
            new LineRange(
                info.toMove2
                    .startLine, // document.getLineNumber(newCodeBlock.getParent().getTextRange().getStartOffset()),
                document.getLineNumber(blockChild.getTextRange().getStartOffset()));
      } else {
        int start =
            document.getLineNumber(newCodeBlock.getRBrace().getTextRange().getStartOffset());
        int end = info.toMove.startLine;
        if (start > end) end = start;
        info.toMove2 = new LineRange(start, end);
      }
    } catch (IncorrectOperationException e) {
      LOG.error(e);
    }
  }
Beispiel #9
0
  private static BraceStatus checkForMovableUpClosingBrace(
      @NotNull PsiElement closingBrace,
      PsiElement block,
      @NotNull Editor editor,
      @NotNull MoveInfo info) {
    //noinspection unchecked
    PsiElement prev = JetPsiUtil.getLastChildByType(block, JetExpression.class);
    if (prev == null) return BraceStatus.NOT_MOVABLE;

    Document doc = editor.getDocument();

    info.toMove = new LineRange(closingBrace, closingBrace, doc);
    info.toMove2 = new LineRange(prev, prev, doc);
    info.indentSource = true;

    return BraceStatus.MOVABLE;
  }
  private int updatedMovedRegionStart(
      final Document document,
      int movedLineStart,
      final int offset,
      @NotNull final MoveInfo info,
      final boolean down) {
    final int line = document.getLineNumber(offset);
    final LineRange toMove = info.toMove;
    int delta = toMove.startLine - line;
    info.toMove = new LineRange(Math.min(line, toMove.startLine), toMove.endLine);

    // update moved range
    if (delta > 0 && !down) {
      final LineRange toMove2 = info.toMove2;
      info.toMove2 = new LineRange(toMove2.startLine - delta, toMove2.endLine - delta);
      movedLineStart = document.getLineStartOffset(toMove.startLine);
    }
    return movedLineStart;
  }
 private void updatedMovedIntoEnd(
     final Document document, @NotNull final MoveInfo info, final int offset) {
   if (offset + 1 < document.getTextLength()) {
     final int line = document.getLineNumber(offset + 1);
     final LineRange toMove2 = info.toMove2;
     if (toMove2 == null) return;
     info.toMove2 =
         new LineRange(
             toMove2.startLine,
             Math.min(Math.max(line, toMove2.endLine), document.getLineCount() - 1));
   }
 }
  /**
   * Handles the mouse location click.
   *
   * @param loc the location that was clicked
   * @return true because the click has been handled
   */
  @Override
  public boolean locationClicked(Location loc) {
    if (game.getTurn() instanceof SmartComputerCheckerPlayer) // ignore all clicks on CPU's turn
    {
      return true;
    }

    if (lastMove != null
        && lastMove.isJump()
        && lastMove.getPiece().canJump()) // locks selection onto jump-chaining piece
    {
      Piece lastPiece = lastMove.getPiece();

      if (!loc.equals(lastPiece.getLocation())
          && !lastPiece.getAllowedMoves().contains(loc)) // consumes irrelevant clicks
      {
        return true;
      }
    }
    if (getGrid().get(loc) != null && !(getGrid().get(loc) instanceof PieceTile)) {
      Piece p = getGrid().get(loc);
      if (pieceSelected) {
        game.undisplayMoves(playerPiece);
      }
      game.displayMoves(p); // selection and highlight available moves
      playerPiece = p;
      pieceSelected = true;
    } else if (pieceSelected && !playerPiece.getAllowedMoves().contains(loc)) {
      pieceSelected = false;
      game.undisplayMoves(playerPiece); // undo selection and highlight
      playerPiece = null;
    } else if (pieceSelected && playerPiece.getAllowedMoves().contains(loc)) {
      pieceSelected = false;
      game.undisplayMoves(playerPiece); // undo selection and highlight
      setPlayerLocation(loc);
    }

    return true;
  }
  private int updateMovedRegionEnd(
      final Document document,
      int movedLineStart,
      final int valueStart,
      @NotNull final MoveInfo info,
      final boolean down) {
    final int line = document.getLineNumber(valueStart);
    final LineRange toMove = info.toMove;
    int delta = line - toMove.endLine;
    info.toMove = new LineRange(toMove.startLine, Math.max(line, toMove.endLine));

    // update moved range
    if (delta > 0 && down) {
      final LineRange toMove2 = info.toMove2;
      info.toMove2 =
          new LineRange(
              toMove2.startLine + delta,
              Math.min(toMove2.endLine + delta, document.getLineCount() - 1));
      movedLineStart = document.getLineStartOffset(toMove.startLine);
    }
    return movedLineStart;
  }
Beispiel #14
0
  /**
   * Convert a chess move string to a Move object. The string may specify any combination of
   * piece/source/target/promotion information as long as it matches exactly one valid move.
   */
  public static final Move stringToMove(Position pos, String strMove) {
    if (strMove.equals("--")) return new Move(0, 0, 0);

    strMove = strMove.replaceAll("=", "");
    strMove = strMove.replaceAll("\\+", "");
    strMove = strMove.replaceAll("#", "");
    boolean wtm = pos.whiteMove;

    MoveInfo info = new MoveInfo();
    boolean capture = false;
    if (strMove.equals("O-O") || strMove.equals("0-0") || strMove.equals("o-o")) {
      info.piece = wtm ? Piece.WKING : Piece.BKING;
      info.fromX = 4;
      info.toX = 6;
      info.fromY = info.toY = wtm ? 0 : 7;
      info.promPiece = Piece.EMPTY;
    } else if (strMove.equals("O-O-O") || strMove.equals("0-0-0") || strMove.equals("o-o-o")) {
      info.piece = wtm ? Piece.WKING : Piece.BKING;
      info.fromX = 4;
      info.toX = 2;
      info.fromY = info.toY = wtm ? 0 : 7;
      info.promPiece = Piece.EMPTY;
    } else {
      boolean atToSq = false;
      for (int i = 0; i < strMove.length(); i++) {
        char c = strMove.charAt(i);
        if (i == 0) {
          int piece = charToPiece(wtm, c);
          if (piece >= 0) {
            info.piece = piece;
            continue;
          }
        }
        int tmpX = c - 'a';
        if ((tmpX >= 0) && (tmpX < 8)) {
          if (atToSq || (info.fromX >= 0)) info.toX = tmpX;
          else info.fromX = tmpX;
        }
        int tmpY = c - '1';
        if ((tmpY >= 0) && (tmpY < 8)) {
          if (atToSq || (info.fromY >= 0)) info.toY = tmpY;
          else info.fromY = tmpY;
        }
        if ((c == 'x') || (c == '-')) {
          atToSq = true;
          if (c == 'x') capture = true;
        }
        if (i == strMove.length() - 1) {
          int promPiece = charToPiece(wtm, c);
          if (promPiece >= 0) {
            info.promPiece = promPiece;
          }
        }
      }
      if ((info.fromX >= 0) && (info.toX < 0)) {
        info.toX = info.fromX;
        info.fromX = -1;
      }
      if ((info.fromY >= 0) && (info.toY < 0)) {
        info.toY = info.fromY;
        info.fromY = -1;
      }
      if (info.piece < 0) {
        boolean haveAll =
            (info.fromX >= 0) && (info.fromY >= 0) && (info.toX >= 0) && (info.toY >= 0);
        if (!haveAll) info.piece = wtm ? Piece.WPAWN : Piece.BPAWN;
      }
      if (info.promPiece < 0) info.promPiece = Piece.EMPTY;
    }

    ArrayList<Move> moves = MoveGen.instance.pseudoLegalMoves(pos);
    moves = MoveGen.removeIllegal(pos, moves);

    ArrayList<Move> matches = new ArrayList<Move>(2);
    for (int i = 0; i < moves.size(); i++) {
      Move m = moves.get(i);
      int p = pos.getPiece(m.from);
      boolean match = true;
      if ((info.piece >= 0) && (info.piece != p)) match = false;
      if ((info.fromX >= 0) && (info.fromX != Position.getX(m.from))) match = false;
      if ((info.fromY >= 0) && (info.fromY != Position.getY(m.from))) match = false;
      if ((info.toX >= 0) && (info.toX != Position.getX(m.to))) match = false;
      if ((info.toY >= 0) && (info.toY != Position.getY(m.to))) match = false;
      if ((info.promPiece >= 0) && (info.promPiece != m.promoteTo)) match = false;
      if (match) {
        matches.add(m);
      }
    }
    int nMatches = matches.size();
    if (nMatches == 0) return null;
    else if (nMatches == 1) return matches.get(0);
    if (!capture) return null;
    Move move = null;
    for (int i = 0; i < matches.size(); i++) {
      Move m = matches.get(i);
      int capt = pos.getPiece(m.to);
      if (capt != Piece.EMPTY) {
        if (move == null) move = m;
        else return null;
      }
    }
    return move;
  }
  public boolean checkAvailable(
      @NotNull final Editor editor,
      @NotNull final PsiFile file,
      @NotNull final MoveInfo info,
      final boolean down) {
    if (!(file instanceof XmlFile)) {
      return false;
    }
    boolean available = super.checkAvailable(editor, file, info, down);
    if (!available) return false;

    // updated moved range end to cover multiline tag start
    final Document document = editor.getDocument();
    int movedLineStart = document.getLineStartOffset(info.toMove.startLine);
    final int movedLineEnd = document.getLineEndOffset(info.toMove.endLine - 1);

    PsiElement movedEndElement = file.findElementAt(movedLineEnd);
    if (movedEndElement instanceof PsiWhiteSpace)
      movedEndElement = PsiTreeUtil.prevLeaf(movedEndElement);
    PsiElement movedStartElement = file.findElementAt(movedLineStart);
    if (movedStartElement instanceof PsiWhiteSpace)
      movedStartElement = PsiTreeUtil.nextLeaf(movedStartElement);

    if (movedEndElement == null || movedStartElement == null) return false;
    final PsiNamedElement namedParentAtEnd =
        PsiTreeUtil.getParentOfType(movedEndElement, PsiNamedElement.class);
    final PsiNamedElement namedParentAtStart =
        PsiTreeUtil.getParentOfType(movedStartElement, PsiNamedElement.class);

    final XmlText text = PsiTreeUtil.getParentOfType(movedStartElement, XmlText.class);
    final XmlText text2 = PsiTreeUtil.getParentOfType(movedEndElement, XmlText.class);

    // Let's do not care about injections for this mover
    if ((text != null && InjectedLanguageUtil.getInjectedPsiFiles(text) != null)
        || (text2 != null && InjectedLanguageUtil.getInjectedPsiFiles(text2) != null)) {
      return false;
    }

    XmlTag nearestTag = PsiTreeUtil.getParentOfType(movedStartElement, XmlTag.class);
    if (nearestTag != null
        && ("script".equals(nearestTag.getLocalName())
            || (nearestTag instanceof HtmlTag
                && "script".equalsIgnoreCase(nearestTag.getLocalName())))) {
      return false;
    }

    PsiNamedElement movedParent = null;

    if (namedParentAtEnd == namedParentAtStart) movedParent = namedParentAtEnd;
    else if (namedParentAtEnd instanceof XmlAttribute
        && namedParentAtStart instanceof XmlTag
        && namedParentAtEnd.getParent() == namedParentAtStart) {
      movedParent = namedParentAtStart;
    } else if (namedParentAtStart instanceof XmlAttribute
        && namedParentAtEnd instanceof XmlTag
        && namedParentAtStart.getParent() == namedParentAtEnd) {
      movedParent = namedParentAtEnd;
    }

    if (movedParent == null) {
      return false;
    }

    final TextRange textRange = movedParent.getTextRange();

    if (movedParent instanceof XmlTag) {
      final XmlTag tag = (XmlTag) movedParent;
      final TextRange valueRange = tag.getValue().getTextRange();
      final int valueStart = valueRange.getStartOffset();

      if (movedLineStart < valueStart && valueStart + 1 < document.getTextLength()) {
        movedLineStart = updateMovedRegionEnd(document, movedLineStart, valueStart + 1, info, down);
      }
      if (movedLineStart < valueStart) {
        movedLineStart =
            updatedMovedRegionStart(
                document, movedLineStart, tag.getTextRange().getStartOffset(), info, down);
      }
    } else if (movedParent instanceof XmlAttribute) {
      final int endOffset = textRange.getEndOffset() + 1;
      if (endOffset < document.getTextLength())
        movedLineStart = updateMovedRegionEnd(document, movedLineStart, endOffset, info, down);
      movedLineStart =
          updatedMovedRegionStart(document, movedLineStart, textRange.getStartOffset(), info, down);
    }

    final TextRange moveDestinationRange =
        new TextRange(
            document.getLineStartOffset(info.toMove2.startLine),
            document.getLineStartOffset(info.toMove2.endLine));

    if (movedParent instanceof XmlAttribute) {
      final XmlTag parent = ((XmlAttribute) movedParent).getParent();

      if (parent != null) {
        final TextRange valueRange = parent.getValue().getTextRange();

        // Do not move attributes out of tags
        if ((down && moveDestinationRange.getEndOffset() >= valueRange.getStartOffset())
            || (!down
                && moveDestinationRange.getStartOffset()
                    <= parent.getTextRange().getStartOffset())) {
          info.toMove2 = null;
        }
      }
    }

    if (down) {
      PsiElement updatedElement = file.findElementAt(moveDestinationRange.getEndOffset());
      if (updatedElement instanceof PsiWhiteSpace)
        updatedElement = PsiTreeUtil.prevLeaf(updatedElement);

      if (updatedElement != null) {
        final PsiNamedElement namedParent =
            PsiTreeUtil.getParentOfType(updatedElement, movedParent.getClass());

        if (namedParent instanceof XmlTag) {
          final XmlTag tag = (XmlTag) namedParent;
          final int offset =
              tag.isEmpty()
                  ? tag.getTextRange().getStartOffset()
                  : tag.getValue().getTextRange().getStartOffset();
          updatedMovedIntoEnd(document, info, offset);
        } else if (namedParent instanceof XmlAttribute) {
          updatedMovedIntoEnd(document, info, namedParent.getTextRange().getEndOffset());
        }
      }
    } else {
      PsiElement updatedElement = file.findElementAt(moveDestinationRange.getStartOffset());
      if (updatedElement instanceof PsiWhiteSpace)
        updatedElement = PsiTreeUtil.nextLeaf(updatedElement);

      if (updatedElement != null) {
        final PsiNamedElement namedParent =
            PsiTreeUtil.getParentOfType(updatedElement, movedParent.getClass());

        if (namedParent instanceof XmlTag) {
          final XmlTag tag = (XmlTag) namedParent;
          final TextRange tagValueRange = tag.getValue().getTextRange();

          // We need to update destination range to jump over tag start
          final XmlTag[] subtags = tag.getSubTags();
          if ((tagValueRange.contains(movedLineStart)
                  && subtags.length > 0
                  && subtags[0] == movedParent)
              || (tagValueRange.getLength() == 0
                  && tag.getTextRange().intersects(moveDestinationRange))) {
            final int line = document.getLineNumber(tag.getTextRange().getStartOffset());
            final LineRange toMove2 = info.toMove2;
            info.toMove2 = new LineRange(Math.min(line, toMove2.startLine), toMove2.endLine);
          }
        } else if (namedParent instanceof XmlAttribute) {
          final int line = document.getLineNumber(namedParent.getTextRange().getStartOffset());
          final LineRange toMove2 = info.toMove2;
          info.toMove2 = new LineRange(Math.min(line, toMove2.startLine), toMove2.endLine);
        }
      }
    }

    return true;
  }