private static void doCommitTransaction(
      @NotNull Document document, @NotNull DocumentChangeTransaction documentChangeTransaction) {
    DocumentEx ex = (DocumentEx) document;
    ex.suppressGuardedExceptions();
    try {
      boolean isReadOnly = !document.isWritable();
      ex.setReadOnly(false);
      final Set<Pair<MutableTextRange, StringBuffer>> affectedFragments =
          documentChangeTransaction.getAffectedFragments();
      for (final Pair<MutableTextRange, StringBuffer> pair : affectedFragments) {
        final StringBuffer replaceBuffer = pair.getSecond();
        final MutableTextRange range = pair.getFirst();
        if (replaceBuffer.length() == 0) {
          ex.deleteString(range.getStartOffset(), range.getEndOffset());
        } else if (range.getLength() == 0) {
          ex.insertString(range.getStartOffset(), replaceBuffer);
        } else {
          ex.replaceString(range.getStartOffset(), range.getEndOffset(), replaceBuffer);
        }
      }

      ex.setReadOnly(isReadOnly);
      // if(documentChangeTransaction.getChangeScope() != null) {
      //
      // LOG.assertTrue(document.getText().equals(documentChangeTransaction.getChangeScope().getText()),
      //                 "Psi to document synchronization failed (send to IK)");
      // }
    } finally {
      ex.unSuppressGuardedExceptions();
    }
  }
    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();
    }
    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;
    }