parse method

  1. @override
List<LinkifyElement> parse(
  1. List<LinkifyElement> elements,
  2. LinkifyOptions options
)
override

Implementation

@override
List<LinkifyElement> parse(
    List<LinkifyElement> elements, LinkifyOptions options) {
  final list = <LinkifyElement>[];

  for (var element in elements) {
    if (element is TextElement) {
      String remaining = element.text;

      // === Bullet List Mode ===
      if (options.linkAsList) {
        // First, check if this text contains multiple hash links in a list format
        final allMatches = _listItemCoreRegex.allMatches(remaining).toList();

        if (allMatches.length < 2) {
          // Less than 2 links, use inline parsing
          final matches = _inlineResourceRegex.allMatches(remaining);
          if (matches.isEmpty) {
            list.add(TextElement(remaining));
          } else {
            var currentIndex = 0;
            for (final match in matches) {
              final prefix = remaining.substring(currentIndex, match.start);
              if (prefix.isNotEmpty) {
                list.add(TextElement(prefix));
              }
              list.add(HashUrlElement(match.group(1)!, match.group(2)!));
              currentIndex = match.end;
            }
            final suffix = remaining.substring(currentIndex);
            if (suffix.isNotEmpty) {
              list.add(TextElement(suffix));
            }
          }
          continue;
        }

        // Check if links are consecutive (separated only by whitespace, newlines, or commas)
        bool isConsecutiveList = true;
        for (int i = 0; i < allMatches.length - 1; i++) {
          final currentEnd = allMatches[i].end;
          final nextStart = allMatches[i + 1].start;
          final textBetween =
              remaining.substring(currentEnd, nextStart).trim();

          // Links should be separated by nothing, comma, or just whitespace/newlines
          if (textBetween.isNotEmpty && textBetween != ',') {
            isConsecutiveList = false;
            break;
          }
        }

        // If not a consecutive list, use inline parsing
        if (!isConsecutiveList) {
          final matches = _inlineResourceRegex.allMatches(remaining);
          if (matches.isEmpty) {
            list.add(TextElement(remaining));
          } else {
            var currentIndex = 0;
            for (final match in matches) {
              final prefix = remaining.substring(currentIndex, match.start);
              if (prefix.isNotEmpty) {
                list.add(TextElement(prefix));
              }
              list.add(HashUrlElement(match.group(1)!, match.group(2)!));
              currentIndex = match.end;
            }
            final suffix = remaining.substring(currentIndex);
            if (suffix.isNotEmpty) {
              list.add(TextElement(suffix));
            }
          }
          continue;
        }

        // We have multiple consecutive links, process as a list
        while (remaining.isNotEmpty) {
          // Find the next newline
          final nextNewline = remaining.indexOf('\n');
          final line = nextNewline >= 0
              ? remaining.substring(0, nextNewline)
              : remaining;

          // Try to find a link pattern in this line
          final coreMatch = _listItemCoreRegex.firstMatch(line);

          if (coreMatch != null) {
            final beforeLink = line.substring(0, coreMatch.start).trim();
            final url = coreMatch.group(1)!;
            final rawTitle = coreMatch.group(2)!;
            final title = rawTitle.trim();
            final afterLink = line.substring(coreMatch.end).trim();

            // Add text before the link (if any)
            if (beforeLink.isNotEmpty) {
              list.add(TextElement(beforeLink));
              list.add(TextElement('\n'));
            }

            // Add the bullet and link
            list.add(TextElement('• '));
            list.add(HashUrlElement(
              url,
              title.isNotEmpty ? title : url,
            ));

            // Handle text after the link
            if (afterLink.isNotEmpty) {
              // Check if it's just punctuation
              if (afterLink == ',' || afterLink == '.' || afterLink == ';') {
                list.add(TextElement(afterLink));
              } else {
                // It's substantial text, put it on next line
                list.add(TextElement('\n'));
                list.add(TextElement(afterLink));
              }
            }

            // Add newline after bullet point
            list.add(TextElement('\n'));

            // Advance remaining
            remaining =
                nextNewline >= 0 ? remaining.substring(nextNewline + 1) : '';
          } else {
            // No link found in this line, add it as-is
            list.add(TextElement(line));
            if (nextNewline >= 0) {
              list.add(TextElement('\n'));
              remaining = remaining.substring(nextNewline + 1);
            } else {
              remaining = '';
            }
          }
        }
        continue;
      }

      // === Fallback: Original Inline #url#text# Parsing ===
      final matches = _inlineResourceRegex.allMatches(remaining);

      if (matches.isEmpty) {
        list.add(TextElement(remaining));
      } else {
        var currentIndex = 0;
        for (final match in matches) {
          final prefix = remaining.substring(currentIndex, match.start);
          if (prefix.isNotEmpty) {
            list.add(TextElement(prefix));
          }

          list.add(HashUrlElement(match.group(1)!, match.group(2)!));

          currentIndex = match.end;
        }

        final suffix = remaining.substring(currentIndex);
        if (suffix.isNotEmpty) {
          list.add(TextElement(suffix));
        }
      }
    } else {
      list.add(element);
    }
  }

  return list;
}