private Pair<MutableTextRange, StringBuffer> getFragmentByRange(int start, final int length) {
      final StringBuffer fragmentBuffer = new StringBuffer();
      int end = start + length;

      // restoring buffer and remove all subfragments from the list
      int documentOffset = 0;
      int effectiveOffset = 0;

      Iterator<Pair<MutableTextRange, StringBuffer>> iterator = myAffectedFragments.iterator();
      while (iterator.hasNext() && effectiveOffset <= end) {
        final Pair<MutableTextRange, StringBuffer> pair = iterator.next();
        final MutableTextRange range = pair.getFirst();
        final StringBuffer buffer = pair.getSecond();
        int effectiveFragmentEnd = range.getStartOffset() + buffer.length();

        if (range.getStartOffset() <= start && effectiveFragmentEnd >= end) return pair;

        if (effectiveFragmentEnd >= start) {
          final int effectiveStart = Math.max(effectiveOffset, start);
          if (range.getStartOffset() > start) {
            fragmentBuffer.append(
                myDocument.getCharsSequence(),
                effectiveStart - effectiveOffset + documentOffset,
                Math.min(range.getStartOffset(), end) - effectiveOffset + documentOffset);
          }
          if (end >= range.getStartOffset()) {
            fragmentBuffer.append(buffer);
            end =
                end > effectiveFragmentEnd
                    ? end - (buffer.length() - range.getLength())
                    : range.getEndOffset();
            effectiveFragmentEnd = range.getEndOffset();
            start = Math.min(start, range.getStartOffset());
            iterator.remove();
          }
        }

        documentOffset += range.getEndOffset() - effectiveOffset;
        effectiveOffset = effectiveFragmentEnd;
      }

      if (effectiveOffset < end) {
        final int effectiveStart = Math.max(effectiveOffset, start);
        fragmentBuffer.append(
            myDocument.getCharsSequence(),
            effectiveStart - effectiveOffset + documentOffset,
            end - effectiveOffset + documentOffset);
      }

      MutableTextRange newRange = new MutableTextRange(start, end);
      final Pair<MutableTextRange, StringBuffer> pair =
          new Pair<MutableTextRange, StringBuffer>(newRange, fragmentBuffer);
      for (Pair<MutableTextRange, StringBuffer> affectedFragment : myAffectedFragments) {
        MutableTextRange range = affectedFragment.getFirst();
        assert end <= range.getStartOffset() || range.getEndOffset() <= start
            : "Range :" + range + "; Added: " + newRange;
      }
      myAffectedFragments.add(pair);
      return pair;
    }
 private static LineRange expandLineRangeToCoverPsiElements(
     final LineRange range, Editor editor, final PsiFile file) {
   Pair<PsiElement, PsiElement> psiRange = getElementRange(editor, file, range);
   if (psiRange == null) return null;
   final PsiElement parent =
       PsiTreeUtil.findCommonParent(psiRange.getFirst(), psiRange.getSecond());
   Pair<PsiElement, PsiElement> elementRange =
       getElementRange(parent, psiRange.getFirst(), psiRange.getSecond());
   if (elementRange == null) return null;
   int endOffset = elementRange.getSecond().getTextRange().getEndOffset();
   Document document = editor.getDocument();
   if (endOffset > document.getTextLength()) {
     LOG.assertTrue(!PsiDocumentManager.getInstance(file.getProject()).isUncommited(document));
     LOG.assertTrue(PsiDocumentManagerImpl.checkConsistency(file, document));
   }
   int endLine;
   if (endOffset == document.getTextLength()) {
     endLine = document.getLineCount();
   } else {
     endLine = editor.offsetToLogicalPosition(endOffset).line + 1;
     endLine = Math.min(endLine, document.getLineCount());
   }
   int startLine =
       Math.min(
           range.startLine,
           editor.offsetToLogicalPosition(elementRange.getFirst().getTextOffset()).line);
   endLine = Math.max(endLine, range.endLine);
   return new LineRange(startLine, endLine);
 }
  @Override
  @NotNull
  public String getPlainText() {
    int startOffset = getNavigationOffset();
    final PsiElement element = getElement();
    if (element != null && startOffset != -1) {
      final Document document = getDocument();
      if (document != null) {
        int lineNumber = document.getLineNumber(startOffset);
        int lineStart = document.getLineStartOffset(lineNumber);
        int lineEnd = document.getLineEndOffset(lineNumber);
        String prefixSuffix = null;

        if (lineEnd - lineStart > ChunkExtractor.MAX_LINE_LENGTH_TO_SHOW) {
          prefixSuffix = "...";
          lineStart =
              Math.max(
                  startOffset - ChunkExtractor.OFFSET_BEFORE_TO_SHOW_WHEN_LONG_LINE, lineStart);
          lineEnd =
              Math.min(startOffset + ChunkExtractor.OFFSET_AFTER_TO_SHOW_WHEN_LONG_LINE, lineEnd);
        }
        String s = document.getCharsSequence().subSequence(lineStart, lineEnd).toString();
        if (prefixSuffix != null) s = prefixSuffix + s + prefixSuffix;
        return s;
      }
    }
    return UsageViewBundle.message("node.invalid");
  }
  private static void logStats(Collection<PsiFile> otherFiles, long start) {
    long time = System.currentTimeMillis() - start;

    final Multiset<String> stats = HashMultiset.create();
    for (PsiFile file : otherFiles) {
      stats.add(
          StringUtil.notNullize(file.getViewProvider().getVirtualFile().getExtension())
              .toLowerCase());
    }

    List<String> extensions = ContainerUtil.newArrayList(stats.elementSet());
    Collections.sort(
        extensions,
        new Comparator<String>() {
          @Override
          public int compare(String o1, String o2) {
            return stats.count(o2) - stats.count(o1);
          }
        });

    String message =
        "Search in "
            + otherFiles.size()
            + " files with unknown types took "
            + time
            + "ms.\n"
            + "Mapping their extensions to an existing file type (e.g. Plain Text) might speed up the search.\n"
            + "Most frequent non-indexed file extensions: ";
    for (int i = 0; i < Math.min(10, extensions.size()); i++) {
      String extension = extensions.get(i);
      message += extension + "(" + stats.count(extension) + ") ";
    }
    LOG.info(message);
  }
 @NotNull
 private static String toVfString(@NotNull Collection<VirtualFile> list) {
   List<VirtualFile> sub = new ArrayList<VirtualFile>(list).subList(0, Math.min(list.size(), 100));
   return list.size()
       + " files: "
       + StringUtil.join(sub, file -> file.getName(), ", ")
       + (list.size() == sub.size() ? "" : "...");
 }
    private String getText(final int start, final int end) {
      int currentOldDocumentOffset = 0;
      int currentNewDocumentOffset = 0;
      StringBuilder text = new StringBuilder();
      Iterator<Pair<MutableTextRange, StringBuffer>> iterator = myAffectedFragments.iterator();
      while (iterator.hasNext() && currentNewDocumentOffset < end) {
        final Pair<MutableTextRange, StringBuffer> pair = iterator.next();
        final MutableTextRange range = pair.getFirst();
        final StringBuffer buffer = pair.getSecond();
        final int fragmentEndInNewDocument = range.getStartOffset() + buffer.length();

        if (range.getStartOffset() <= start && fragmentEndInNewDocument >= end) {
          return buffer.substring(start - range.getStartOffset(), end - range.getStartOffset());
        }

        if (range.getStartOffset() >= start) {
          final int effectiveStart = Math.max(currentNewDocumentOffset, start);
          text.append(
              myDocument.getCharsSequence(),
              effectiveStart - currentNewDocumentOffset + currentOldDocumentOffset,
              Math.min(range.getStartOffset(), end)
                  - currentNewDocumentOffset
                  + currentOldDocumentOffset);
          if (end > range.getStartOffset()) {
            text.append(
                buffer.substring(0, Math.min(end - range.getStartOffset(), buffer.length())));
          }
        }

        currentOldDocumentOffset += range.getEndOffset() - currentNewDocumentOffset;
        currentNewDocumentOffset = fragmentEndInNewDocument;
      }

      if (currentNewDocumentOffset < end) {
        final int effectiveStart = Math.max(currentNewDocumentOffset, start);
        text.append(
            myDocument.getCharsSequence(),
            effectiveStart - currentNewDocumentOffset + currentOldDocumentOffset,
            end - currentNewDocumentOffset + currentOldDocumentOffset);
      }

      return text.toString();
    }
 int getNavigationOffset() {
   Document document = getDocument();
   if (document == null) return -1;
   int offset = getUsageInfo().getNavigationOffset();
   if (offset == -1) offset = myOffset;
   if (offset >= document.getTextLength()) {
     int line = Math.max(0, Math.min(myLineNumber, document.getLineCount() - 1));
     offset = document.getLineStartOffset(line);
   }
   return offset;
 }
Example #8
0
 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));
   }
 }
 private boolean isWhiteSpaceOrComment(@NotNull PsiElement element, @NotNull TextRange range) {
   final TextRange textRange = element.getTextRange();
   TextRange intersection = range.intersection(textRange);
   if (intersection == null) {
     return false;
   }
   intersection =
       TextRange.create(
           Math.max(intersection.getStartOffset() - textRange.getStartOffset(), 0),
           Math.min(
               intersection.getEndOffset() - textRange.getStartOffset(), textRange.getLength()));
   return isWhiteSpaceOrComment(element)
       || intersection.substring(element.getText()).trim().length() == 0;
 }
    private int psiToDocumentOffset(int offset) {
      for (Map.Entry<TextRange, CharSequence> entry : myAffectedFragments.entrySet()) {
        int lengthAfter = entry.getValue().length();
        TextRange range = entry.getKey();
        if (range.getStartOffset() + lengthAfter < offset) {
          offset += range.getLength() - lengthAfter;
          continue;
        }

        // for offsets inside replaced ranges, return the starts of the original affected fragments
        // in document
        return Math.min(range.getStartOffset(), offset);
      }
      return offset;
    }
  private static int calcMatch(final String expectedName, final List<String> words, int max) {
    if (expectedName == null) return max;

    String[] expectedWords = NameUtil.nameToWords(expectedName);
    int limit = Math.min(words.size(), expectedWords.length);
    for (int i = 0; i < limit; i++) {
      String word = words.get(words.size() - i - 1);
      String expectedWord = expectedWords[expectedWords.length - i - 1];
      if (word.equalsIgnoreCase(expectedWord)) {
        max = Math.max(max, i + 1);
      } else {
        break;
      }
    }
    return max;
  }
Example #12
0
  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;
  }
Example #13
0
    @Override
    public void exchangeRows(int row, int targetRow) {
      if (row < 0 || row >= getVariableData().length) return;
      if (targetRow < 0 || targetRow >= getVariableData().length) return;

      final VariableData currentItem = getVariableData()[row];
      getVariableData()[row] = getVariableData()[targetRow];
      getVariableData()[targetRow] = currentItem;

      TypeSelector currentSelector = myParameterTypeSelectors[row];
      myParameterTypeSelectors[row] = myParameterTypeSelectors[targetRow];
      myParameterTypeSelectors[targetRow] = currentSelector;

      myTypeRendererCombo.setModel(new DefaultComboBoxModel(getVariableData()));

      myTableModel.fireTableRowsUpdated(Math.min(targetRow, row), Math.max(targetRow, row));
      myTable.getSelectionModel().setSelectionInterval(targetRow, targetRow);
      updateSignature();
    }
 private void queueUnresolvedFilesSinceLastRestart() {
   PersistentFS fs = PersistentFS.getInstance();
   int maxId = FSRecords.getMaxId();
   TIntArrayList list = new TIntArrayList();
   for (int id = fileIsResolved.nextClearBit(1);
       id >= 0 && id < maxId;
       id = fileIsResolved.nextClearBit(id + 1)) {
     int nextSetBit = fileIsResolved.nextSetBit(id);
     int endOfRun = Math.min(maxId, nextSetBit == -1 ? maxId : nextSetBit);
     do {
       VirtualFile virtualFile = fs.findFileById(id);
       if (queueIfNeeded(virtualFile, myProject)) {
         list.add(id);
       } else {
         fileIsResolved.set(id);
       }
     } while (++id < endOfRun);
   }
   log("Initially added to resolve " + toVfString(list.toNativeArray()));
 }
  private static PsiStatement getAnchor(PsiExpression[] expressionOccurences) {
    PsiElement parent = expressionOccurences[0];
    int minOffset = expressionOccurences[0].getTextRange().getStartOffset();
    for (int i = 1; i < expressionOccurences.length; i++) {
      parent = PsiTreeUtil.findCommonParent(parent, expressionOccurences[i]);
      LOG.assertTrue(parent != null);
      minOffset = Math.min(minOffset, expressionOccurences[i].getTextRange().getStartOffset());
    }

    PsiCodeBlock block =
        (PsiCodeBlock)
            (parent instanceof PsiCodeBlock
                ? parent
                : PsiTreeUtil.getParentOfType(parent, PsiCodeBlock.class));
    LOG.assertTrue(block != null && block.getStatements().length > 0);
    PsiStatement[] statements = block.getStatements();
    for (int i = 1; i < statements.length; i++) {
      if (statements[i].getTextRange().getStartOffset() > minOffset) return statements[i - 1];
    }
    return statements[statements.length - 1];
  }
Example #16
0
  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;
  }
  static void commentNestedComments(
      @NotNull Document document, TextRange range, Commenter commenter) {
    final int offset = range.getStartOffset();
    final IntArrayList toReplaceWithComments = new IntArrayList();
    final IntArrayList prefixes = new IntArrayList();

    final String text =
        document
            .getCharsSequence()
            .subSequence(range.getStartOffset(), range.getEndOffset())
            .toString();
    final String commentedPrefix = commenter.getCommentedBlockCommentPrefix();
    final String commentedSuffix = commenter.getCommentedBlockCommentSuffix();
    final String commentPrefix = commenter.getBlockCommentPrefix();
    final String commentSuffix = commenter.getBlockCommentSuffix();

    int nearestSuffix = getNearest(text, commentedSuffix, 0);
    int nearestPrefix = getNearest(text, commentedPrefix, 0);
    int level = 0;
    int lastSuffix = -1;
    for (int i = Math.min(nearestPrefix, nearestSuffix);
        i < text.length();
        i = Math.min(nearestPrefix, nearestSuffix)) {
      if (i > nearestPrefix) {
        nearestPrefix = getNearest(text, commentedPrefix, i);
        continue;
      }
      if (i > nearestSuffix) {
        nearestSuffix = getNearest(text, commentedSuffix, i);
        continue;
      }
      if (i == nearestPrefix) {
        if (level <= 0) {
          if (lastSuffix != -1) {
            toReplaceWithComments.add(lastSuffix);
          }
          level = 1;
          lastSuffix = -1;
          toReplaceWithComments.add(i);
          prefixes.add(i);
        } else {
          level++;
        }
        nearestPrefix = getNearest(text, commentedPrefix, nearestPrefix + 1);
      } else {
        lastSuffix = i;
        level--;
        nearestSuffix = getNearest(text, commentedSuffix, nearestSuffix + 1);
      }
    }
    if (lastSuffix != -1) {
      toReplaceWithComments.add(lastSuffix);
    }

    int prefixIndex = prefixes.size() - 1;
    for (int i = toReplaceWithComments.size() - 1; i >= 0; i--) {
      int position = toReplaceWithComments.get(i);
      if (prefixIndex >= 0 && position == prefixes.get(prefixIndex)) {
        prefixIndex--;
        document.replaceString(
            offset + position, offset + position + commentedPrefix.length(), commentPrefix);
      } else {
        document.replaceString(
            offset + position, offset + position + commentedSuffix.length(), commentSuffix);
      }
    }
  }
Example #18
0
  @Nullable
  public static PsiType getLeastUpperBound(
      @NotNull PsiType type1, @NotNull PsiType type2, PsiManager manager) {
    if (type1 instanceof GrTupleType && type2 instanceof GrTupleType) {
      GrTupleType tuple1 = (GrTupleType) type1;
      GrTupleType tuple2 = (GrTupleType) type2;
      PsiType[] components1 = tuple1.getComponentTypes();
      PsiType[] components2 = tuple2.getComponentTypes();

      if (components1.length == 0) return genNewListBy(type2, manager);
      if (components2.length == 0) return genNewListBy(type1, manager);

      PsiType[] components3 = new PsiType[Math.min(components1.length, components2.length)];
      for (int i = 0; i < components3.length; i++) {
        PsiType c1 = components1[i];
        PsiType c2 = components2[i];
        if (c1 == null || c2 == null) {
          components3[i] = null;
        } else {
          components3[i] = getLeastUpperBound(c1, c2, manager);
        }
      }
      return new GrTupleType(
          components3,
          JavaPsiFacade.getInstance(manager.getProject()),
          tuple1.getScope().intersectWith(tuple2.getResolveScope()));
    } else if (checkEmptyListAndList(type1, type2)) {
      return genNewListBy(type2, manager);
    } else if (checkEmptyListAndList(type2, type1)) {
      return genNewListBy(type1, manager);
    } else if (type1 instanceof GrMapType && type2 instanceof GrMapType) {
      return GrMapType.merge(((GrMapType) type1), ((GrMapType) type2));
    } else if (checkEmptyMapAndMap(type1, type2)) {
      return genNewMapBy(type2, manager);
    } else if (checkEmptyMapAndMap(type2, type1)) {
      return genNewMapBy(type1, manager);
    } else if (type1 instanceof GrClosureType && type2 instanceof GrClosureType) {
      GrClosureType clType1 = (GrClosureType) type1;
      GrClosureType clType2 = (GrClosureType) type2;
      GrSignature signature1 = clType1.getSignature();
      GrSignature signature2 = clType2.getSignature();

      if (signature1 instanceof GrClosureSignature && signature2 instanceof GrClosureSignature) {
        if (((GrClosureSignature) signature1).getParameterCount()
            == ((GrClosureSignature) signature2).getParameterCount()) {
          final GrClosureSignature signature =
              GrClosureSignatureImpl.getLeastUpperBound(
                  ((GrClosureSignature) signature1), ((GrClosureSignature) signature2), manager);
          if (signature != null) {
            GlobalSearchScope scope =
                clType1.getResolveScope().intersectWith(clType2.getResolveScope());
            final LanguageLevel languageLevel =
                ComparatorUtil.max(clType1.getLanguageLevel(), clType2.getLanguageLevel());
            return GrClosureType.create(
                signature,
                scope,
                JavaPsiFacade.getInstance(manager.getProject()),
                languageLevel,
                true);
          }
        }
      }
    } else if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(type1.getCanonicalText())
        && CommonClassNames.JAVA_LANG_STRING.equals(type2.getInternalCanonicalText())) {
      return type2;
    } else if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(type2.getCanonicalText())
        && CommonClassNames.JAVA_LANG_STRING.equals(type1.getInternalCanonicalText())) {
      return type1;
    }
    final PsiType result = getLeastUpperBoundForNumericType(type1, type2);
    if (result != null) return result;
    return GenericsUtil.getLeastUpperBound(type1, type2, manager);
  }
Example #19
0
  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;
  }
  private Specifics isMoreSpecific(
      @NotNull MethodCandidateInfo info1,
      @NotNull MethodCandidateInfo info2,
      @MethodCandidateInfo.ApplicabilityLevelConstant int applicabilityLevel,
      @NotNull LanguageLevel languageLevel) {
    PsiMethod method1 = info1.getElement();
    PsiMethod method2 = info2.getElement();
    final PsiClass class1 = method1.getContainingClass();
    final PsiClass class2 = method2.getContainingClass();

    final PsiParameter[] params1 = method1.getParameterList().getParameters();
    final PsiParameter[] params2 = method2.getParameterList().getParameters();

    final PsiTypeParameter[] typeParameters1 = method1.getTypeParameters();
    final PsiTypeParameter[] typeParameters2 = method2.getTypeParameters();
    final PsiSubstitutor classSubstitutor1 =
        info1.getSubstitutor(false); // substitutions for method type parameters will be ignored
    final PsiSubstitutor classSubstitutor2 = info2.getSubstitutor(false);

    final int max = Math.max(params1.length, params2.length);
    PsiType[] types1 = PsiType.createArray(max);
    PsiType[] types2 = PsiType.createArray(max);
    final boolean varargsPosition =
        applicabilityLevel == MethodCandidateInfo.ApplicabilityLevel.VARARGS;
    for (int i = 0; i < max; i++) {
      ProgressManager.checkCanceled();
      PsiType type1 =
          params1.length > 0 ? params1[Math.min(i, params1.length - 1)].getType() : null;
      PsiType type2 =
          params2.length > 0 ? params2[Math.min(i, params2.length - 1)].getType() : null;
      if (varargsPosition) {
        if (type1 instanceof PsiEllipsisType
            && type2 instanceof PsiEllipsisType
            && params1.length == params2.length
            && class1 != null
            && (!JavaVersionService.getInstance().isAtLeast(class1, JavaSdkVersion.JDK_1_7)
                || ((PsiArrayType) type1)
                    .getComponentType()
                    .equalsToText(CommonClassNames.JAVA_LANG_OBJECT)
                || ((PsiArrayType) type2)
                    .getComponentType()
                    .equalsToText(CommonClassNames.JAVA_LANG_OBJECT))) {
          type1 = ((PsiEllipsisType) type1).toArrayType();
          type2 = ((PsiEllipsisType) type2).toArrayType();
        } else {
          type1 =
              type1 instanceof PsiEllipsisType ? ((PsiArrayType) type1).getComponentType() : type1;
          type2 =
              type2 instanceof PsiEllipsisType ? ((PsiArrayType) type2).getComponentType() : type2;
        }
      }

      types1[i] = type1;
      types2[i] = type2;
    }

    boolean sameBoxing = true;
    int[] boxingHappened = new int[2];
    for (int i = 0; i < types1.length; i++) {
      ProgressManager.checkCanceled();
      PsiType type1 = classSubstitutor1.substitute(types1[i]);
      PsiType type2 = classSubstitutor2.substitute(types2[i]);
      PsiType argType = i < getActualParameterTypes().length ? getActualParameterTypes()[i] : null;

      boolean boxingInFirst = false;
      if (isBoxingHappened(argType, type1, languageLevel)) {
        boxingHappened[0] += 1;
        boxingInFirst = true;
      }

      boolean boxingInSecond = false;
      if (isBoxingHappened(argType, type2, languageLevel)) {
        boxingHappened[1] += 1;
        boxingInSecond = true;
      }
      sameBoxing &= boxingInFirst == boxingInSecond;
    }
    if (boxingHappened[0] == 0 && boxingHappened[1] > 0) return Specifics.FIRST;
    if (boxingHappened[0] > 0 && boxingHappened[1] == 0) return Specifics.SECOND;

    if (sameBoxing) {
      final PsiSubstitutor siteSubstitutor1 = info1.getSiteSubstitutor();
      final PsiSubstitutor siteSubstitutor2 = info2.getSiteSubstitutor();

      final PsiType[] types2AtSite = typesAtSite(types2, siteSubstitutor2);
      final PsiType[] types1AtSite = typesAtSite(types1, siteSubstitutor1);

      final PsiSubstitutor methodSubstitutor1 =
          calculateMethodSubstitutor(
              typeParameters1, method1, siteSubstitutor1, types1, types2AtSite, languageLevel);
      boolean applicable12 =
          isApplicableTo(
              types2AtSite, method1, languageLevel, varargsPosition, methodSubstitutor1, method2);

      final PsiSubstitutor methodSubstitutor2 =
          calculateMethodSubstitutor(
              typeParameters2, method2, siteSubstitutor2, types2, types1AtSite, languageLevel);
      boolean applicable21 =
          isApplicableTo(
              types1AtSite, method2, languageLevel, varargsPosition, methodSubstitutor2, method1);

      if (!myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
        final boolean typeArgsApplicable12 =
            GenericsUtil.isTypeArgumentsApplicable(
                typeParameters1, methodSubstitutor1, myArgumentsList, !applicable21);
        final boolean typeArgsApplicable21 =
            GenericsUtil.isTypeArgumentsApplicable(
                typeParameters2, methodSubstitutor2, myArgumentsList, !applicable12);

        if (!typeArgsApplicable12) {
          applicable12 = false;
        }

        if (!typeArgsApplicable21) {
          applicable21 = false;
        }
      }

      if (applicable12 || applicable21) {

        if (applicable12 && !applicable21) return Specifics.SECOND;
        if (applicable21 && !applicable12) return Specifics.FIRST;

        final boolean abstract1 = method1.hasModifierProperty(PsiModifier.ABSTRACT);
        final boolean abstract2 = method2.hasModifierProperty(PsiModifier.ABSTRACT);
        if (abstract1 && !abstract2) {
          return Specifics.SECOND;
        }
        if (abstract2 && !abstract1) {
          return Specifics.FIRST;
        }
      }

      if (languageLevel.isAtLeast(LanguageLevel.JDK_1_8)
          && myArgumentsList instanceof PsiExpressionList
          && (typeParameters1.length == 0 || typeParameters2.length == 0)) {
        boolean toCompareFunctional = false;
        if (types1.length > 0 && types2.length > 0) {
          for (int i = 0; i < getActualParametersLength(); i++) {
            final PsiType type1 = types1[Math.min(i, types1.length - 1)];
            final PsiType type2 = types2[Math.min(i, types2.length - 1)];
            // from 15.12.2.5 Choosing the Most Specific Method
            // In addition, a functional interface type S is more specific than a functional
            // interface type T for an expression exp
            // if T is not a subtype of S and one of the following conditions apply.
            if (LambdaUtil.isFunctionalType(type1)
                && !TypeConversionUtil.erasure(type1).isAssignableFrom(type2)
                && LambdaUtil.isFunctionalType(type2)
                && !TypeConversionUtil.erasure(type2).isAssignableFrom(type1)) {
              types1AtSite[Math.min(i, types1.length - 1)] = PsiType.NULL;
              types2AtSite[Math.min(i, types2.length - 1)] = PsiType.NULL;
              toCompareFunctional = true;
            }
          }
        }

        if (toCompareFunctional) {
          final boolean applicable12ignoreFunctionalType =
              isApplicableTo(
                  types2AtSite,
                  method1,
                  languageLevel,
                  varargsPosition,
                  calculateMethodSubstitutor(
                      typeParameters1,
                      method1,
                      siteSubstitutor1,
                      types1,
                      types2AtSite,
                      languageLevel),
                  null);
          final boolean applicable21ignoreFunctionalType =
              isApplicableTo(
                  types1AtSite,
                  method2,
                  languageLevel,
                  varargsPosition,
                  calculateMethodSubstitutor(
                      typeParameters2,
                      method2,
                      siteSubstitutor2,
                      types2,
                      types1AtSite,
                      languageLevel),
                  null);

          if (applicable12ignoreFunctionalType || applicable21ignoreFunctionalType) {
            Specifics specifics = null;
            for (int i = 0; i < getActualParametersLength(); i++) {
              if (types1AtSite[Math.min(i, types1.length - 1)] == PsiType.NULL
                  && types2AtSite[Math.min(i, types2.length - 1)] == PsiType.NULL) {
                Specifics specific =
                    isFunctionalTypeMoreSpecific(
                        info1, info2, ((PsiExpressionList) myArgumentsList).getExpressions()[i], i);
                if (specific == Specifics.NEITHER) {
                  specifics = Specifics.NEITHER;
                  break;
                }

                if (specifics == null) {
                  specifics = specific;
                } else if (specifics != specific) {
                  specifics = Specifics.NEITHER;
                  break;
                }
              }
            }

            if (!applicable12ignoreFunctionalType && applicable21ignoreFunctionalType) {
              return specifics == Specifics.FIRST ? Specifics.FIRST : Specifics.NEITHER;
            }

            if (!applicable21ignoreFunctionalType && applicable12ignoreFunctionalType) {
              return specifics == Specifics.SECOND ? Specifics.SECOND : Specifics.NEITHER;
            }

            return specifics;
          }
        }
      }
    } else if (varargsPosition) {
      final PsiType lastParamType1 = classSubstitutor1.substitute(types1[types1.length - 1]);
      final PsiType lastParamType2 = classSubstitutor2.substitute(types2[types1.length - 1]);
      final boolean assignable1 = TypeConversionUtil.isAssignable(lastParamType2, lastParamType1);
      final boolean assignable2 = TypeConversionUtil.isAssignable(lastParamType1, lastParamType2);
      if (assignable1 && !assignable2) {
        return Specifics.FIRST;
      }
      if (assignable2 && !assignable1) {
        return Specifics.SECOND;
      }
    }

    if (class1 != class2) {
      if (class2.isInheritor(class1, true) || class1.isInterface() && !class2.isInterface()) {
        if (MethodSignatureUtil.isSubsignature(
            method1.getSignature(info1.getSubstitutor(false)),
            method2.getSignature(info2.getSubstitutor(false)))) {
          return Specifics.SECOND;
        } else if (method1.hasModifierProperty(PsiModifier.STATIC)
            && method2.hasModifierProperty(PsiModifier.STATIC)
            && boxingHappened[0] == 0) {
          return Specifics.SECOND;
        }
      } else if (MethodSignatureUtil.areErasedParametersEqual(
              method1.getSignature(PsiSubstitutor.EMPTY),
              method2.getSignature(PsiSubstitutor.EMPTY))
          && MethodSignatureUtil.isSubsignature(
              method2.getSignature(info2.getSubstitutor(false)),
              method1.getSignature(info1.getSubstitutor(false)))) {
        return Specifics.FIRST;
      } else if (class1.isInheritor(class2, true) || class2.isInterface()) {
        if (method1.hasModifierProperty(PsiModifier.STATIC)
            && method2.hasModifierProperty(PsiModifier.STATIC)
            && boxingHappened[0] == 0) {
          return Specifics.FIRST;
        }
      }
    }

    final boolean raw1 = PsiUtil.isRawSubstitutor(method1, classSubstitutor1);
    final boolean raw2 = PsiUtil.isRawSubstitutor(method2, classSubstitutor2);
    if (raw1 ^ raw2) {
      return raw1 ? Specifics.SECOND : Specifics.FIRST;
    }

    final boolean varargs1 = info1.isVarargs();
    final boolean varargs2 = info2.isVarargs();
    if (varargs1 ^ varargs2) {
      return varargs1 ? Specifics.SECOND : Specifics.FIRST;
    }

    return Specifics.NEITHER;
  }
  private List<PostponedAction> normalizeAndReorderPostponedActions(
      final TreeSet<PostprocessFormattingTask> rangesToProcess, Document document) {
    final List<PostprocessFormattingTask> freeFormatingActions =
        new ArrayList<PostprocessFormattingTask>();
    final List<ReindentTask> indentActions = new ArrayList<ReindentTask>();

    PostprocessFormattingTask accumulatedTask = null;
    Iterator<PostprocessFormattingTask> iterator = rangesToProcess.iterator();
    while (iterator.hasNext()) {
      final PostprocessFormattingTask currentTask = iterator.next();
      if (accumulatedTask == null) {
        accumulatedTask = currentTask;
        iterator.remove();
      } else if (accumulatedTask.getStartOffset() > currentTask.getEndOffset()
          || accumulatedTask.getStartOffset() == currentTask.getEndOffset()
              && !canStickActionsTogether(accumulatedTask, currentTask)) {
        // action can be pushed
        if (accumulatedTask instanceof ReindentTask) {
          indentActions.add((ReindentTask) accumulatedTask);
        } else {
          freeFormatingActions.add(accumulatedTask);
        }

        accumulatedTask = currentTask;
        iterator.remove();
      } else if (accumulatedTask instanceof ReformatTask && currentTask instanceof ReindentTask) {
        // split accumulated reformat range into two
        if (accumulatedTask.getStartOffset() < currentTask.getStartOffset()) {
          final RangeMarker endOfRange =
              document.createRangeMarker(
                  accumulatedTask.getStartOffset(), currentTask.getStartOffset());
          // add heading reformat part
          rangesToProcess.add(new ReformatTask(endOfRange));
          // and manage heading whitespace because formatter does not edit it in previous action
          iterator = rangesToProcess.iterator();
          //noinspection StatementWithEmptyBody
          while (iterator.next().getRange() != currentTask.getRange()) ;
        }
        final RangeMarker rangeToProcess =
            document.createRangeMarker(currentTask.getEndOffset(), accumulatedTask.getEndOffset());
        freeFormatingActions.add(new ReformatWithHeadingWhitespaceTask(rangeToProcess));
        accumulatedTask = currentTask;
        iterator.remove();
      } else {
        if (!(accumulatedTask instanceof ReindentTask)) {
          iterator.remove();

          boolean withLeadingWhitespace =
              accumulatedTask instanceof ReformatWithHeadingWhitespaceTask;
          if (accumulatedTask instanceof ReformatTask
              && currentTask instanceof ReformatWithHeadingWhitespaceTask
              && accumulatedTask.getStartOffset() == currentTask.getStartOffset()) {
            withLeadingWhitespace = true;
          } else if (accumulatedTask instanceof ReformatWithHeadingWhitespaceTask
              && currentTask instanceof ReformatTask
              && accumulatedTask.getStartOffset() < currentTask.getStartOffset()) {
            withLeadingWhitespace = false;
          }
          int newStart = Math.min(accumulatedTask.getStartOffset(), currentTask.getStartOffset());
          int newEnd = Math.max(accumulatedTask.getEndOffset(), currentTask.getEndOffset());
          RangeMarker rangeMarker;

          if (accumulatedTask.getStartOffset() == newStart
              && accumulatedTask.getEndOffset() == newEnd) {
            rangeMarker = accumulatedTask.getRange();
          } else if (currentTask.getStartOffset() == newStart
              && currentTask.getEndOffset() == newEnd) {
            rangeMarker = currentTask.getRange();
          } else {
            rangeMarker = document.createRangeMarker(newStart, newEnd);
          }

          if (withLeadingWhitespace) {
            accumulatedTask = new ReformatWithHeadingWhitespaceTask(rangeMarker);
          } else {
            accumulatedTask = new ReformatTask(rangeMarker);
          }
        } else if (currentTask instanceof ReindentTask) {
          iterator.remove();
        } // TODO[ik]: need to be fixed to correctly process indent inside indent
      }
    }
    if (accumulatedTask != null) {
      if (accumulatedTask instanceof ReindentTask) {
        indentActions.add((ReindentTask) accumulatedTask);
      } else {
        freeFormatingActions.add(accumulatedTask);
      }
    }

    final List<PostponedAction> result = new ArrayList<PostponedAction>();
    Collections.reverse(freeFormatingActions);
    Collections.reverse(indentActions);

    if (!freeFormatingActions.isEmpty()) {
      FormatTextRanges ranges = new FormatTextRanges();
      for (PostprocessFormattingTask action : freeFormatingActions) {
        TextRange range = TextRange.create(action);
        ranges.add(range, action instanceof ReformatWithHeadingWhitespaceTask);
      }
      result.add(new ReformatRangesAction(ranges));
    }

    if (!indentActions.isEmpty()) {
      ReindentRangesAction reindentRangesAction = new ReindentRangesAction();
      for (ReindentTask action : indentActions) {
        reindentRangesAction.add(action.getRange(), action.getOldIndent());
      }
      result.add(reindentRangesAction);
    }

    return result;
  }
    @Override
    public void run() {
      CaretModel caretModel = myEditor.getCaretModel();
      try {
        final CharSequence chars = myDocument.getCharsSequence();
        int i = CharArrayUtil.shiftBackwardUntil(chars, myOffset - 1, LINE_SEPARATOR) - 1;
        i = CharArrayUtil.shiftBackwardUntil(chars, i, LINE_SEPARATOR) + 1;
        if (i < 0) i = 0;
        int lineStart = CharArrayUtil.shiftForward(chars, i, " \t");
        CodeDocumentationUtil.CommentContext commentContext =
            CodeDocumentationUtil.tryParseCommentContext(myFile, chars, myOffset, lineStart);

        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(getProject());
        if (commentContext.docStart) {
          PsiElement element = myFile.findElementAt(commentContext.lineStart);
          final String text = element.getText();
          final PsiElement parent = element.getParent();

          if (text.equals(commentContext.commenter.getDocumentationCommentPrefix())
                  && isDocComment(parent, commentContext.commenter)
              || text.startsWith(commentContext.commenter.getDocumentationCommentPrefix())
                  && element instanceof PsiComment) {
            PsiComment comment =
                isDocComment(parent, commentContext.commenter)
                    ? (PsiComment) parent
                    : (PsiComment) element;
            int commentEnd = comment.getTextRange().getEndOffset();

            if (myOffset >= commentEnd) {
              commentContext.docStart = false;
            } else {
              if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
                if (myOffset >= commentEnd) {
                  commentContext.docAsterisk = false;
                  commentContext.docStart = false;
                } else {
                  commentContext.docAsterisk = true;
                  commentContext.docStart = false;
                }
              } else {
                generateJavadoc(commentContext.commenter);
              }
            }
          } else {
            commentContext.docStart = false;
          }
        } else if (commentContext.cStyleStart) {
          PsiElement element = myFile.findElementAt(commentContext.lineStart);
          if (element instanceof PsiComment
              && commentContext.commenter.getBlockCommentTokenType()
                  == ((PsiComment) element).getTokenType()) {
            final PsiComment comment = (PsiComment) element;
            int commentEnd = comment.getTextRange().getEndOffset();
            if (myOffset >= commentEnd && myOffset < myFile.getTextRange().getEndOffset()) {
              commentContext.docStart = false;
            } else {
              if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
                if (myOffset >= commentEnd) {
                  commentContext.docAsterisk = false;
                  commentContext.docStart = false;
                } else {
                  commentContext.docAsterisk = true;
                  commentContext.docStart = false;
                }
              } else {
                final int currentEndOfLine = CharArrayUtil.shiftForwardUntil(chars, myOffset, "\n");
                myDocument.insertString(
                    currentEndOfLine, " " + commentContext.commenter.getBlockCommentSuffix());
                int lstart = CharArrayUtil.shiftBackwardUntil(chars, myOffset, "\n");
                myDocument.insertString(currentEndOfLine, chars.subSequence(lstart, myOffset));
                psiDocumentManager.commitDocument(myDocument);
              }
            }
          } else {
            commentContext.docStart = false;
          }
        }

        String indentInsideJavadoc = null;
        if (myOffset < myDocument.getTextLength()) {
          final int line = myDocument.getLineNumber(myOffset);
          if (line > 0 && (commentContext.docAsterisk || commentContext.docStart)) {
            indentInsideJavadoc =
                CodeDocumentationUtil.getIndentInsideJavadoc(
                    myDocument, myDocument.getLineStartOffset(line - 1));
          }
        }

        if (commentContext.docAsterisk) {
          commentContext.docAsterisk =
              insertDocAsterisk(
                  commentContext.lineStart,
                  commentContext.docAsterisk,
                  !StringUtil.isEmpty(indentInsideJavadoc),
                  commentContext.commenter);
        }

        boolean docIndentApplied = false;
        CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance();
        if (codeInsightSettings.SMART_INDENT_ON_ENTER
            || myForceIndent
            || commentContext.docStart
            || commentContext.docAsterisk
            || commentContext.slashSlash) {
          final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(getProject());
          myOffset = codeStyleManager.adjustLineIndent(myFile, myOffset);
          psiDocumentManager.commitAllDocuments();

          if (!StringUtil.isEmpty(indentInsideJavadoc) && myOffset < myDocument.getTextLength()) {
            myDocument.insertString(myOffset + 1, indentInsideJavadoc);
            myOffset += indentInsideJavadoc.length();
            docIndentApplied = true;
          }

          if (myForceIndent && indentInsideJavadoc != null) {
            int indentSize =
                CodeStyleSettingsManager.getSettings(myProject).getIndentSize(myFile.getFileType());
            myDocument.insertString(myOffset + 1, StringUtil.repeatSymbol(' ', indentSize));
            myCaretAdvance += indentSize;
          }
        }

        if ((commentContext.docAsterisk || commentContext.docStart || commentContext.slashSlash)
            && !docIndentApplied) {
          if (myInsertSpace) {
            if (myOffset == myDocument.getTextLength()) {
              myDocument.insertString(myOffset, " ");
            }
            myDocument.insertString(myOffset + 1, " ");
          }

          final char c = myDocument.getCharsSequence().charAt(myOffset);
          if (c != '\n') {
            myOffset += 1;
          }
        }

        if ((commentContext.docAsterisk || commentContext.slashSlash) && !commentContext.docStart) {
          myCaretAdvance +=
              commentContext.slashSlash
                  ? commentContext.commenter.getLineCommentPrefix().length()
                  : 1;
        }
      } catch (IncorrectOperationException e) {
        LOG.error(e);
      }

      myOffset = Math.min(myOffset, myDocument.getTextLength());
      caretModel.moveToOffset(myOffset);
      myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      myEditor.getSelectionModel().removeSelection();
      if (myCaretAdvance != 0) {
        LogicalPosition caretPosition = caretModel.getLogicalPosition();
        LogicalPosition pos =
            new LogicalPosition(caretPosition.line, caretPosition.column + myCaretAdvance);
        caretModel.moveToLogicalPosition(pos);
      }
    }
  /**
   * Generates a comment if possible.
   *
   * <p>It's assumed that this method {@link PsiDocumentManager#commitDocument(Document) syncs} all
   * PSI-document changes during the processing.
   *
   * @param anchor target element for which a comment should be generated
   * @param editor target editor
   * @param commenter commenter to use
   * @param project current project
   */
  private static void generateComment(
      @NotNull PsiElement anchor,
      @NotNull Editor editor,
      @NotNull CodeDocumentationProvider documentationProvider,
      @NotNull CodeDocumentationAwareCommenter commenter,
      @NotNull Project project) {
    Document document = editor.getDocument();
    int commentStartOffset = anchor.getTextRange().getStartOffset();
    int lineStartOffset = document.getLineStartOffset(document.getLineNumber(commentStartOffset));
    if (lineStartOffset > 0 && lineStartOffset < commentStartOffset) {
      // Example:
      //    void test1() {
      //    }
      //    void test2() {
      //       <offset>
      //    }
      // We want to insert the comment at the start of the line where 'test2()' is declared.
      int nonWhiteSpaceOffset =
          CharArrayUtil.shiftBackward(document.getCharsSequence(), commentStartOffset - 1, " \t");
      commentStartOffset = Math.max(nonWhiteSpaceOffset, lineStartOffset);
    }

    int commentBodyRelativeOffset = 0;
    int caretOffsetToSet = -1;
    StringBuilder buffer = new StringBuilder();
    String commentPrefix = commenter.getDocumentationCommentPrefix();
    if (commentPrefix != null) {
      buffer.append(commentPrefix).append("\n");
      commentBodyRelativeOffset += commentPrefix.length() + 1;
    }

    String linePrefix = commenter.getDocumentationCommentLinePrefix();
    if (linePrefix != null) {
      buffer.append(linePrefix);
      commentBodyRelativeOffset += linePrefix.length();
      caretOffsetToSet = commentStartOffset + commentBodyRelativeOffset;
    }
    buffer.append("\n");
    commentBodyRelativeOffset++;

    String commentSuffix = commenter.getDocumentationCommentSuffix();
    if (commentSuffix != null) {
      buffer.append(commentSuffix).append("\n");
    }

    if (buffer.length() <= 0) {
      return;
    }

    document.insertString(commentStartOffset, buffer);
    PsiDocumentManager docManager = PsiDocumentManager.getInstance(project);
    docManager.commitDocument(document);

    Pair<PsiElement, PsiComment> pair = documentationProvider.parseContext(anchor);
    if (pair == null || pair.second == null) {
      return;
    }

    String stub = documentationProvider.generateDocumentationContentStub(pair.second);
    CaretModel caretModel = editor.getCaretModel();
    if (stub != null) {
      int insertionOffset = commentStartOffset + commentBodyRelativeOffset;
      // if (CodeStyleSettingsManager.getSettings(project).JD_ADD_BLANK_AFTER_DESCRIPTION) {
      //  buffer.setLength(0);
      //  if (linePrefix != null) {
      //    buffer.append(linePrefix);
      //  }
      //  buffer.append("\n");
      //  buffer.append(stub);
      //  stub = buffer.toString();
      // }
      document.insertString(insertionOffset, stub);
      docManager.commitDocument(document);
      pair = documentationProvider.parseContext(anchor);
    }

    if (caretOffsetToSet >= 0) {
      caretModel.moveToOffset(caretOffsetToSet);
    }

    if (pair == null || pair.second == null) {
      return;
    }

    int start = Math.min(calcStartReformatOffset(pair.first), calcStartReformatOffset(pair.second));
    int end = pair.second.getTextRange().getEndOffset();

    CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
    codeStyleManager.reformatText(anchor.getContainingFile(), start, end);

    int caretOffset = caretModel.getOffset();
    if (caretOffset > 0 && caretOffset <= document.getTextLength()) {
      char c = document.getCharsSequence().charAt(caretOffset - 1);
      if (!StringUtil.isWhiteSpace(c)) {
        document.insertString(caretOffset, " ");
        caretModel.moveToOffset(caretOffset + 1);
      }
    }
  }
  /**
   * This method searches ast node that could be reparsed incrementally and returns pair of target
   * reparseable node and new replacement node. Returns null if there is no any chance to make
   * incremental parsing.
   */
  @Nullable
  public Couple<ASTNode> findReparseableRoots(
      @NotNull PsiFileImpl file,
      @NotNull TextRange changedPsiRange,
      @NotNull CharSequence newFileText) {
    Project project = file.getProject();
    final FileElement fileElement = file.getTreeElement();
    final CharTable charTable = fileElement.getCharTable();
    int lengthShift = newFileText.length() - fileElement.getTextLength();

    if (fileElement.getElementType() instanceof ITemplateDataElementType || isTooDeep(file)) {
      // unable to perform incremental reparse for template data in JSP, or in exceptionally deep
      // trees
      return null;
    }

    final ASTNode leafAtStart =
        fileElement.findLeafElementAt(Math.max(0, changedPsiRange.getStartOffset() - 1));
    final ASTNode leafAtEnd =
        fileElement.findLeafElementAt(
            Math.min(changedPsiRange.getEndOffset(), fileElement.getTextLength() - 1));
    ASTNode node =
        leafAtStart != null && leafAtEnd != null
            ? TreeUtil.findCommonParent(leafAtStart, leafAtEnd)
            : fileElement;
    Language baseLanguage = file.getViewProvider().getBaseLanguage();

    while (node != null && !(node instanceof FileElement)) {
      IElementType elementType = node.getElementType();
      if (elementType instanceof IReparseableElementType) {
        final TextRange textRange = node.getTextRange();
        final IReparseableElementType reparseable = (IReparseableElementType) elementType;

        if (baseLanguage.isKindOf(reparseable.getLanguage())
            && textRange.getLength() + lengthShift > 0) {
          final int start = textRange.getStartOffset();
          final int end = start + textRange.getLength() + lengthShift;
          if (end > newFileText.length()) {
            reportInconsistentLength(file, newFileText, node, start, end);
            break;
          }

          CharSequence newTextStr = newFileText.subSequence(start, end);

          if (reparseable.isParsable(node.getTreeParent(), newTextStr, baseLanguage, project)) {
            ASTNode chameleon = reparseable.createNode(newTextStr);
            if (chameleon != null) {
              DummyHolder holder =
                  DummyHolderFactory.createHolder(
                      file.getManager(), null, node.getPsi(), charTable);
              holder.getTreeElement().rawAddChildren((TreeElement) chameleon);

              if (holder.getTextLength() != newTextStr.length()) {
                String details =
                    ApplicationManager.getApplication().isInternal()
                        ? "text=" + newTextStr + "; treeText=" + holder.getText() + ";"
                        : "";
                LOG.error("Inconsistent reparse: " + details + " type=" + elementType);
              }

              return Couple.of(node, chameleon);
            }
          }
        }
      }
      node = node.getTreeParent();
    }
    return null;
  }
  private static Pair<Set<String>, Set<TextWithImports>> findReferencedVars(
      Set<String> visibleVars, SourcePosition position) {
    final int line = position.getLine();
    if (line < 0) {
      return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
    }
    final PsiFile positionFile = position.getFile();
    if (!positionFile.getLanguage().isKindOf(JavaLanguage.INSTANCE)) {
      return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
    }

    final VirtualFile vFile = positionFile.getVirtualFile();
    final Document doc =
        vFile != null ? FileDocumentManager.getInstance().getDocument(vFile) : null;
    if (doc == null || doc.getLineCount() == 0 || line > (doc.getLineCount() - 1)) {
      return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
    }

    final TextRange limit = calculateLimitRange(positionFile, doc, line);

    int startLine = Math.max(limit.getStartOffset(), line - 1);
    startLine = Math.min(startLine, limit.getEndOffset());
    while (startLine > limit.getStartOffset() && shouldSkipLine(positionFile, doc, startLine)) {
      startLine--;
    }
    final int startOffset = doc.getLineStartOffset(startLine);

    int endLine = Math.min(line + 2, limit.getEndOffset());
    while (endLine < limit.getEndOffset() && shouldSkipLine(positionFile, doc, endLine)) {
      endLine++;
    }
    final int endOffset = doc.getLineEndOffset(endLine);

    final TextRange lineRange = new TextRange(startOffset, endOffset);
    if (!lineRange.isEmpty()) {
      final int offset =
          CharArrayUtil.shiftForward(doc.getCharsSequence(), doc.getLineStartOffset(line), " \t");
      PsiElement element = positionFile.findElementAt(offset);
      if (element != null) {
        PsiMethod method = PsiTreeUtil.getNonStrictParentOfType(element, PsiMethod.class);
        if (method != null) {
          element = method;
        } else {
          PsiField field = PsiTreeUtil.getNonStrictParentOfType(element, PsiField.class);
          if (field != null) {
            element = field;
          } else {
            final PsiClassInitializer initializer =
                PsiTreeUtil.getNonStrictParentOfType(element, PsiClassInitializer.class);
            if (initializer != null) {
              element = initializer;
            }
          }
        }

        //noinspection unchecked
        if (element instanceof PsiCompiledElement) {
          return Pair.create(visibleVars, Collections.<TextWithImports>emptySet());
        } else {
          VariablesCollector collector =
              new VariablesCollector(visibleVars, adjustRange(element, lineRange));
          element.accept(collector);
          return Pair.create(collector.getVars(), collector.getExpressions());
        }
      }
    }
    return Pair.create(Collections.<String>emptySet(), Collections.<TextWithImports>emptySet());
  }