parse method
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;
}