visitElementBefore method

  1. @override
bool visitElementBefore(
  1. Element element
)

Called when an Element has been reached, before its children have been visited.

Returns false to skip its children.

Implementation

@override
bool visitElementBefore(Element element) {
  _elementStack.add(element);

  switch (element.tag) {
    // Block elements
    case 'h1':
    case 'h2':
    case 'h3':
    case 'h4':
    case 'h5':
    case 'h6':
      _ensureNewline();
      _startHeading(element.tag);
      return true;

    case 'p':
      _ensureNewline();
      if (_inBlockquote) {
        _writeBlockquotePrefix();
      }
      // Start paragraph buffering for text wrapping
      if (options.width != null) {
        _inParagraph = true;
        _paragraphBuffer.clear();
      }
      _startTextStyle();
      return true;

    case 'blockquote':
      _ensureNewline();
      _inBlockquote = true;
      _blockquoteDepth++;
      return true;

    case 'pre':
      _ensureNewline();
      _startCodeBlock(element);
      return true;

    case 'ul':
      if (_listDepth == 0) {
        _ensureNewline();
      } else {
        // Nested list: add newline after parent item text
        _buffer.write('\n');
      }
      _listDepth++;
      return true;

    case 'ol':
      if (_listDepth == 0) {
        _ensureNewline();
      } else {
        // Nested list: add newline after parent item text
        _buffer.write('\n');
      }
      _listDepth++;
      _listCounters.add(
        int.tryParse(element.attributes['start'] ?? '1') ?? 1,
      );
      return true;

    case 'li':
      _startListItem(element);
      _startTextStyle();
      return true;

    case 'hr':
      _ensureNewline();
      _renderHorizontalRule();
      return false; // No children to visit

    case 'table':
      _ensureNewline();
      _tableHeaders.clear();
      _tableRows.clear();
      _tableAlignments.clear();
      return true;

    case 'thead':
      _inTableHeader = true;
      return true;

    case 'tbody':
      _inTableHeader = false;
      return true;

    case 'tr':
      _currentTableRow.clear();
      return true;

    case 'th':
      _inTableCell = true;
      _currentCellBuffer.clear();
      // Capture alignment from header cells
      final align = element.attributes['align'];
      if (align != null) {
        _tableAlignments.add(_parseTableAlign(align));
      }
      return true;

    case 'td':
      _inTableCell = true;
      _currentCellBuffer.clear();
      return true;

    // Inline elements
    case 'em':
      _startInlineStyle(_getEmphasisStyle());
      return true;

    case 'strong':
      _startInlineStyle(_getStrongStyle());
      return true;

    case 'code':
      // Check if inside a pre block (code block vs inline code)
      if (!_isInsidePreBlock()) {
        _startInlineStyle(_getCodeStyle());
      }
      return true;

    case 'a':
      _pendingLinkUrl = element.attributes['href'];
      _startLink();
      return true;

    case 'del':
      _startInlineStyle(_getStrikethroughStyle());
      return true;

    case 'br':
      _buffer.write('\n');
      if (_inBlockquote) {
        _writeBlockquotePrefix();
      }
      return false;

    case 'img':
      _renderImage(element);
      return false;

    case 'input':
      // Task list checkbox
      final checked = element.attributes['checked'] != null;
      final char = checked
          ? options.checkboxChecked
          : options.checkboxUnchecked;
      _buffer.write(char);
      _buffer.write(' ');
      return false;

    default:
      return true;
  }
}