private LeafBlockWrapper doProcessSimpleBlock(
      final Block rootBlock,
      @Nullable final CompositeBlockWrapper parent,
      final boolean readOnly,
      final int index,
      @Nullable Block parentBlock) {
    if (!INLINE_TABS_ENABLED && !myCurrentWhiteSpace.containsLineFeeds()) {
      myCurrentWhiteSpace.setForceSkipTabulationsUsage(true);
    }
    final LeafBlockWrapper info =
        new LeafBlockWrapper(
            rootBlock, parent, myCurrentWhiteSpace, myModel, myOptions, myPreviousBlock, readOnly);
    if (index == 0) {
      info.arrangeParentTextRange();
    }

    switch (myFormatterTagHandler.getFormatterTag(rootBlock)) {
      case ON:
        myInsideFormatRestrictingTag = false;
        break;
      case OFF:
        myInsideFormatRestrictingTag = true;
        break;
      case NONE:
        break;
    }

    TextRange textRange = rootBlock.getTextRange();
    if (textRange.getLength() == 0) {
      assertInvalidRanges(
          textRange.getStartOffset(), textRange.getEndOffset(), myModel, "empty block");
    }
    if (myPreviousBlock != null) {
      myPreviousBlock.setNextBlock(info);
    }
    if (myFirstTokenBlock == null) {
      myFirstTokenBlock = info;
    }
    myLastTokenBlock = info;
    if (currentWhiteSpaceIsReadOnly()) {
      myCurrentWhiteSpace.setReadOnly(true);
    }
    if (myCurrentSpaceProperty != null) {
      myCurrentWhiteSpace.setIsSafe(myCurrentSpaceProperty.isSafe());
      myCurrentWhiteSpace.setKeepFirstColumn(myCurrentSpaceProperty.shouldKeepFirstColumn());
    }

    if (info.isEndOfCodeBlock()) {
      myCurrentWhiteSpace.setBeforeCodeBlockEnd(true);
    }

    info.setSpaceProperty(myCurrentSpaceProperty);
    myCurrentWhiteSpace = new WhiteSpace(textRange.getEndOffset(), false);
    if (myInsideFormatRestrictingTag) myCurrentWhiteSpace.setReadOnly(true);
    myPreviousBlock = info;

    if (myPositionOfInterest != -1
        && (textRange.contains(myPositionOfInterest)
            || textRange.getEndOffset() == myPositionOfInterest)) {
      myResult.put(info, rootBlock);
      if (parent != null) myResult.put(parent, parentBlock);
    }
    return info;
  }
  /**
   * Wraps given root block and all of its descendants and returns root block wrapper.
   *
   * <p>This method performs necessary infrastructure actions and delegates actual processing to
   * {@link #buildCompositeBlock(Block, CompositeBlockWrapper, int, WrapImpl, boolean)} and {@link
   * #processSimpleBlock(Block, CompositeBlockWrapper, boolean, int, Block)}.
   *
   * @param rootBlock block to wrap
   * @param index index of the current block at its parent block. <code>-1</code> may be used here
   *     if we don't have information about parent block
   * @param parent parent block wrapper. <code>null</code> may be used here we no parent block
   *     wrapper exists
   * @param currentWrapParent parent wrap if any; <code>null</code> otherwise
   * @param parentBlock parent block of the block to wrap
   * @param rootBlockIsRightBlock flag that shows if target block is the right-most block
   * @return wrapper for the given <code>'rootBlock'</code>
   */
  private AbstractBlockWrapper buildFrom(
      final Block rootBlock,
      final int index,
      @Nullable final CompositeBlockWrapper parent,
      @Nullable WrapImpl currentWrapParent,
      @Nullable final Block parentBlock,
      boolean rootBlockIsRightBlock) {
    final WrapImpl wrap = (WrapImpl) rootBlock.getWrap();
    if (wrap != null) {
      wrap.registerParent(currentWrapParent);
      currentWrapParent = wrap;
    }
    TextRange textRange = rootBlock.getTextRange();
    final int blockStartOffset = textRange.getStartOffset();

    if (parent != null) {
      if (textRange.getStartOffset() < parent.getStartOffset()) {
        assertInvalidRanges(
            textRange.getStartOffset(),
            parent.getStartOffset(),
            myModel,
            "child block start is less than parent block start");
      }

      if (textRange.getEndOffset() > parent.getEndOffset()) {
        assertInvalidRanges(
            textRange.getEndOffset(),
            parent.getEndOffset(),
            myModel,
            "child block end is after parent block end");
      }
    }

    myCurrentWhiteSpace.append(blockStartOffset, myModel, myOptions);

    if (myCollectAlignmentsInsideFormattingRange
        && rootBlock.getAlignment() != null
        && isAffectedByFormatting(rootBlock)
        && !myInsideFormatRestrictingTag) {
      myAlignmentsInsideRangeToModify.add(rootBlock.getAlignment());
    }

    if (rootBlock.getAlignment() != null) {
      myBlocksToAlign.putValue(rootBlock.getAlignment(), rootBlock);
    }

    ReadOnlyBlockInformationProvider previousProvider = myReadOnlyBlockInformationProvider;
    try {
      if (rootBlock instanceof ReadOnlyBlockInformationProvider) {
        myReadOnlyBlockInformationProvider = (ReadOnlyBlockInformationProvider) rootBlock;
      }
      if (isInsideFormattingRanges(rootBlock, rootBlockIsRightBlock)
          || myCollectAlignmentsInsideFormattingRange && isInsideExtendedAffectedRange(rootBlock)) {
        final List<Block> subBlocks = rootBlock.getSubBlocks();
        if (subBlocks.isEmpty()
            || myReadOnlyBlockInformationProvider != null
                && myReadOnlyBlockInformationProvider.isReadOnly(rootBlock)) {
          final AbstractBlockWrapper wrapper =
              processSimpleBlock(rootBlock, parent, false, index, parentBlock);
          if (!subBlocks.isEmpty()) {
            wrapper.setIndent((IndentImpl) subBlocks.get(0).getIndent());
          }
          return wrapper;
        }
        return buildCompositeBlock(
            rootBlock, parent, index, currentWrapParent, rootBlockIsRightBlock);
      } else {
        // block building is skipped
        return processSimpleBlock(rootBlock, parent, true, index, parentBlock);
      }
    } finally {
      myReadOnlyBlockInformationProvider = previousProvider;
    }
  }