docs: private void _recurse (@Nonnull final Block aRoot, final boolean bListMode)
    {
      Block aBlock;
      Block aList;
      Line aLine = aRoot.m_aLines;
  
      if (bListMode)
      {
        aRoot.removeListIndent (m_bUseExtensions);
        if (m_bUseExtensions && aRoot.m_aLines != null && aRoot.m_aLines.getLineType (m_bUseExtensions) != ELineType.CODE)
        {
          aRoot.m_sID = aRoot.m_aLines.stripID ();
        }
      }
  
      while (aLine != null && aLine.m_bIsEmpty)
        aLine = aLine.m_aNext;
      if (aLine == null)
        return;
  
      while (aLine != null)
      {
        final ELineType eType = aLine.getLineType (m_bUseExtensions);
        switch (eType)
        {
          case OTHER:
          {
            final boolean bWasEmpty = aLine.m_bPrevEmpty;
            while (aLine != null && !aLine.m_bIsEmpty)
            {
              final ELineType eType2 = aLine.getLineType (m_bUseExtensions);
              if ((bListMode || m_bUseExtensions) && (eType2 == ELineType.OLIST || eType2 == ELineType.ULIST))
                break;
              if (m_bUseExtensions &&
                  (eType2 == ELineType.CODE || eType2 == ELineType.FENCED_CODE || eType2 == ELineType.PLUGIN))
                break;
              if (eType2 == ELineType.HEADLINE ||
                  eType2 == ELineType.HEADLINE1 ||
                  eType2 == ELineType.HEADLINE2 ||
                  eType2 == ELineType.HR ||
                  eType2 == ELineType.BQUOTE ||
                  eType2 == ELineType.XML ||
                  eType2 == ELineType.XML_COMMENT)
                break;
              aLine = aLine.m_aNext;
            }
            final EBlockType eBlockType;
            if (aLine != null && !aLine.m_bIsEmpty)
            {
              eBlockType = (bListMode && !bWasEmpty) ? EBlockType.NONE : EBlockType.PARAGRAPH;
              aRoot.split (aLine.m_aPrevious).m_eType = eBlockType;
              aRoot.removeLeadingEmptyLines ();
            }
            else
            {
              eBlockType = (bListMode && (aLine == null || !aLine.m_bIsEmpty) && !bWasEmpty) ? EBlockType.NONE
                                                                                             : EBlockType.PARAGRAPH;
              aRoot.split (aLine == null ? aRoot.m_aLineTail : aLine).m_eType = eBlockType;
              aRoot.removeLeadingEmptyLines ();
            }
            aLine = aRoot.m_aLines;
            break;
          }
          case CODE:
            while (aLine != null && (aLine.m_bIsEmpty || aLine.m_nLeading > 3))
            {
              aLine = aLine.m_aNext;
            }
            aBlock = aRoot.split (aLine != null ? aLine.m_aPrevious : aRoot.m_aLineTail);
            aBlock.m_eType = EBlockType.CODE;
            aBlock.removeSurroundingEmptyLines ();
            break;
          case XML:
          case XML_COMMENT:
            if (aLine.m_aPrevious != null)
            {
              // FIXME ... this looks wrong
              aRoot.split (aLine.m_aPrevious);
            }
            aRoot.split (aLine.m_aXmlEndLine).m_eType = eType == ELineType.XML ? EBlockType.XML : EBlockType.XML_COMMENT;
            aRoot.removeLeadingEmptyLines ();
            aLine = aRoot.m_aLines;
            break;
          case BQUOTE:
            while (aLine != null)
            {
              if (!aLine.m_bIsEmpty &&
                  aLine.m_bPrevEmpty &&
                  aLine.m_nLeading == 0 &&
                  aLine.getLineType (m_bUseExtensions) != ELineType.BQUOTE)
                break;
              aLine = aLine.m_aNext;
            }
            aBlock = aRoot.split (aLine != null ? aLine.m_aPrevious : aRoot.m_aLineTail);
            aBlock.m_eType = EBlockType.BLOCKQUOTE;
            aBlock.removeSurroundingEmptyLines ();
            aBlock.removeBlockQuotePrefix ();
            _recurse (aBlock, false);
            aLine = aRoot.m_aLines;
            break;
          case HR:
            if (aLine.m_aPrevious != null)
            {
              // FIXME ... this looks wrong
              aRoot.split (aLine.m_aPrevious);
            }
            aRoot.split (aLine).m_eType = EBlockType.RULER;
            aRoot.removeLeadingEmptyLines ();
            aLine = aRoot.m_aLines;
            break;
          case FENCED_CODE:
            aLine = aLine.m_aNext;
            while (aLine != null)
            {
              if (aLine.getLineType (m_bUseExtensions) == ELineType.FENCED_CODE)
                break;
              // TODO ... is this really necessary? Maybe add a special
              // flag?
              aLine = aLine.m_aNext;
            }
            if (aLine != null)
              aLine = aLine.m_aNext;
            aBlock = aRoot.split (aLine != null ? aLine.m_aPrevious : aRoot.m_aLineTail);
            aBlock.m_eType = EBlockType.FENCED_CODE;
            aBlock.m_sMeta = MarkdownHelper.getMetaFromFence (aBlock.m_aLines.m_sValue);
            aBlock.m_aLines.setEmpty ();
            if (aBlock.m_aLineTail.getLineType (m_bUseExtensions) == ELineType.FENCED_CODE)
              aBlock.m_aLineTail.setEmpty ();
            aBlock.removeSurroundingEmptyLines ();
            break;
          case PLUGIN:
            aLine = aLine.m_aNext;
            while (aLine != null)
            {
              if (aLine.getLineType (m_bUseExtensions) == ELineType.PLUGIN)
                break;
              // TODO ... is this really necessary? Maybe add a special
              // flag?
              aLine = aLine.m_aNext;
            }
            if (aLine != null)
              aLine = aLine.m_aNext;
            aBlock = aRoot.split (aLine != null ? aLine.m_aPrevious : aRoot.m_aLineTail);
            aBlock.m_eType = EBlockType.PLUGIN;
            aBlock.m_sMeta = MarkdownHelper.getMetaFromFence (aBlock.m_aLines.m_sValue);
            aBlock.m_aLines.setEmpty ();
            if (aBlock.m_aLineTail.getLineType (m_bUseExtensions) == ELineType.PLUGIN)
              aBlock.m_aLineTail.setEmpty ();
            aBlock.removeSurroundingEmptyLines ();
            break;
          case HEADLINE:
          case HEADLINE1:
          case HEADLINE2:
            if (aLine.m_aPrevious != null)
              aRoot.split (aLine.m_aPrevious);
            if (eType != ELineType.HEADLINE)
              aLine.m_aNext.setEmpty ();
            aBlock = aRoot.split (aLine);
            aBlock.m_eType = EBlockType.HEADLINE;
            if (eType != ELineType.HEADLINE)
              aBlock.m_nHeadlineDepth = eType == ELineType.HEADLINE1 ? 1 : 2;
            if (m_bUseExtensions)
              aBlock.m_sID = aBlock.m_aLines.stripID ();
            aBlock.transfromHeadline ();
            aRoot.removeLeadingEmptyLines ();
            aLine = aRoot.m_aLines;
            break;
          case OLIST:
          case ULIST:
            while (aLine != null)
            {
              final ELineType eType2 = aLine.getLineType (m_bUseExtensions);
              if (!aLine.m_bIsEmpty &&
                  (aLine.m_bPrevEmpty && aLine.m_nLeading == 0 && !(eType2 == ELineType.OLIST || eType2 == ELineType.ULIST)))
                break;
              aLine = aLine.m_aNext;
            }
            aList = aRoot.split (aLine != null ? aLine.m_aPrevious : aRoot.m_aLineTail);
            aList.m_eType = eType == ELineType.OLIST ? EBlockType.ORDERED_LIST : EBlockType.UNORDERED_LIST;
            aList.m_aLines.m_bPrevEmpty = false;
            aList.removeSurroundingEmptyLines ();
            aList.m_aLines.m_bPrevEmpty = false;
            _initListBlock (aList);
            aBlock = aList.m_aBlocks;
            while (aBlock != null)
            {
              _recurse (aBlock, true);
              aBlock = aBlock.m_aNext;
            }
            aList.expandListParagraphs ();
            break;
          default:
            aLine = aLine.m_aNext;
            break;
        }
      }
    }