docs: private void recurse(final Block root, final boolean listMode)
      {
          Block block, list;
          Line line = root.lines;
  
          if (listMode)
          {
              root.removeListIndent(this.config);
              if (this.useExtensions && root.lines != null && root.lines.getLineType(this.config) != LineType.CODE)
              {
                  root.id = root.lines.stripID();
              }
          }
  
          while (line != null && line.isEmpty)
          {
              line = line.next;
          }
          if (line == null)
          {
              return;
          }
  
          while (line != null)
          {
              final LineType type = line.getLineType(this.config);
              switch (type)
              {
              case OTHER:
              {
                  final boolean wasEmpty = line.prevEmpty;
                  while (line != null && !line.isEmpty)
                  {
                      final LineType t = line.getLineType(this.config);
                      if ((listMode || this.useExtensions) && (t == LineType.OLIST || t == LineType.ULIST))
                      {
                          break;
                      }
                      if (this.useExtensions && (t == LineType.CODE || t == LineType.FENCED_CODE))
                      {
                          break;
                      }
                      if (t == LineType.HEADLINE || t == LineType.HEADLINE1 || t == LineType.HEADLINE2
                              || t == LineType.HR
                              || t == LineType.BQUOTE || t == LineType.XML)
                      {
                          break;
                      }
                      line = line.next;
                  }
                  final BlockType bt;
                  if (line != null && !line.isEmpty)
                  {
                      bt = (listMode && !wasEmpty) ? BlockType.NONE : BlockType.PARAGRAPH;
                      root.split(line.previous).type = bt;
                      root.removeLeadingEmptyLines();
                  }
                  else
                  {
                      bt = (listMode && (line == null || !line.isEmpty) && !wasEmpty) ? BlockType.NONE
                              : BlockType.PARAGRAPH;
                      root.split(line == null ? root.lineTail : line).type = bt;
                      root.removeLeadingEmptyLines();
                  }
                  line = root.lines;
                  break;
              }
              case CODE:
                  while (line != null && (line.isEmpty || line.leading > 3))
                  {
                      line = line.next;
                  }
                  block = root.split(line != null ? line.previous : root.lineTail);
                  block.type = BlockType.CODE;
                  block.removeSurroundingEmptyLines();
                  break;
              case XML:
                  if (line.previous != null)
                  {
                      // FIXME ... this looks wrong
                      root.split(line.previous);
                  }
                  root.split(line.xmlEndLine).type = BlockType.XML;
                  root.removeLeadingEmptyLines();
                  line = root.lines;
                  break;
              case BQUOTE:
                  while (line != null)
                  {
                      if (!line.isEmpty
                              && (line.prevEmpty && line.leading == 0 && line.getLineType(this.config) != LineType.BQUOTE))
                      {
                          break;
                      }
                      line = line.next;
                  }
                  block = root.split(line != null ? line.previous : root.lineTail);
                  block.type = BlockType.BLOCKQUOTE;
                  block.removeSurroundingEmptyLines();
                  block.removeBlockQuotePrefix();
                  this.recurse(block, false);
                  line = root.lines;
                  break;
              case HR:
                  if (line.previous != null)
                  {
                      // FIXME ... this looks wrong
                      root.split(line.previous);
                  }
                  root.split(line).type = BlockType.RULER;
                  root.removeLeadingEmptyLines();
                  line = root.lines;
                  break;
              case FENCED_CODE:
                  line = line.next;
                  while (line != null)
                  {
                      if (line.getLineType(this.config) == LineType.FENCED_CODE)
                      {
                          break;
                      }
                      // TODO ... is this really necessary? Maybe add a special
                      // flag?
                      line = line.next;
                  }
                  if (line != null)
                  {
                      line = line.next;
                  }
                  block = root.split(line != null ? line.previous : root.lineTail);
                  block.type = BlockType.FENCED_CODE;
                  block.meta = Utils.getMetaFromFence(block.lines.value);
                  block.lines.setEmpty();
                  if (block.lineTail.getLineType(this.config) == LineType.FENCED_CODE)
                  {
                      block.lineTail.setEmpty();
                  }
                  block.removeSurroundingEmptyLines();
                  break;
              case HEADLINE:
              case HEADLINE1:
              case HEADLINE2:
                  if (line.previous != null)
                  {
                      root.split(line.previous);
                  }
                  if (type != LineType.HEADLINE)
                  {
                      line.next.setEmpty();
                  }
                  block = root.split(line);
                  block.type = BlockType.HEADLINE;
                  if (type != LineType.HEADLINE)
                  {
                      block.hlDepth = type == LineType.HEADLINE1 ? 1 : 2;
                  }
                  if (this.useExtensions)
                  {
                      block.id = block.lines.stripID();
                  }
                  block.transfromHeadline();
                  root.removeLeadingEmptyLines();
                  line = root.lines;
                  break;
              case OLIST:
              case ULIST:
                  while (line != null)
                  {
                      final LineType t = line.getLineType(this.config);
                      if (!line.isEmpty
                              && (line.prevEmpty && line.leading == 0 && !(t == LineType.OLIST || t == LineType.ULIST)))
                      {
                          break;
                      }
                      line = line.next;
                  }
                  list = root.split(line != null ? line.previous : root.lineTail);
                  list.type = type == LineType.OLIST ? BlockType.ORDERED_LIST : BlockType.UNORDERED_LIST;
                  list.lines.prevEmpty = false;
                  list.lineTail.nextEmpty = false;
                  list.removeSurroundingEmptyLines();
                  list.lines.prevEmpty = list.lineTail.nextEmpty = false;
                  this.initListBlock(list);
                  block = list.blocks;
                  while (block != null)
                  {
                      this.recurse(block, true);
                      block = block.next;
                  }
                  list.expandListParagraphs();
                  break;
              default:
                  line = line.next;
                  break;
              }
          }
      }