mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-08-06 15:21:21 +08:00

With this I'm not accepting any of the Link's field to be an empty string. In that case it's better for it to be null, as the compiler will force me to deal with an empty value.
172 lines
3.8 KiB
Dart
172 lines
3.8 KiB
Dart
import 'package:markdown/markdown.dart' as md;
|
|
|
|
// FIXME: This should be split into 2 classes, that way it would be easier
|
|
// to access to members with null safety
|
|
class Link {
|
|
String? publicTerm;
|
|
String? filePath;
|
|
String? headingID;
|
|
String? alt;
|
|
|
|
String? wikiTerm;
|
|
|
|
Link({
|
|
required this.publicTerm,
|
|
required this.filePath,
|
|
this.headingID,
|
|
this.alt,
|
|
}) {
|
|
if (publicTerm?.isEmpty == true) {
|
|
publicTerm = null;
|
|
}
|
|
if (headingID?.isEmpty == true) {
|
|
headingID = null;
|
|
}
|
|
if (alt?.isEmpty == true) {
|
|
alt = null;
|
|
}
|
|
}
|
|
Link.wiki(this.wikiTerm);
|
|
|
|
bool get isWikiLink => wikiTerm != null;
|
|
|
|
@override
|
|
int get hashCode => filePath.hashCode;
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is Link &&
|
|
runtimeType == other.runtimeType &&
|
|
filePath == other.filePath &&
|
|
publicTerm == other.publicTerm &&
|
|
wikiTerm == other.wikiTerm &&
|
|
headingID == other.headingID &&
|
|
alt == other.alt;
|
|
|
|
@override
|
|
String toString() {
|
|
return wikiTerm != null
|
|
? 'WikiLink($wikiTerm)'
|
|
: 'Link{publicTerm: $publicTerm, filePath: $filePath, headingID: $headingID, alt: $alt}';
|
|
}
|
|
}
|
|
|
|
class LinkExtractor implements md.NodeVisitor {
|
|
final String filePath;
|
|
List<Link> links = [];
|
|
|
|
LinkExtractor(this.filePath);
|
|
|
|
@override
|
|
bool visitElementBefore(md.Element element) {
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
void visitText(md.Text text) {}
|
|
|
|
@override
|
|
void visitElementAfter(md.Element el) {
|
|
final String tag = el.tag;
|
|
|
|
if (tag == 'a') {
|
|
var type = el.attributes['type'] ?? "";
|
|
if (type == "wiki") {
|
|
var term = el.attributes['term'];
|
|
var link = Link.wiki(term);
|
|
|
|
links.add(link);
|
|
return;
|
|
}
|
|
|
|
var alt = el.attributes['title'];
|
|
var title = _getText(el.children);
|
|
|
|
var url = el.attributes['href'];
|
|
if (url == null || isExternalLink(url)) {
|
|
return;
|
|
}
|
|
|
|
if (url.startsWith('#') || url.startsWith('//')) {
|
|
// FIXME: The heading ID seems incorrect
|
|
var link = Link(
|
|
publicTerm: title,
|
|
filePath: filePath,
|
|
alt: alt,
|
|
headingID: url,
|
|
);
|
|
links.add(link);
|
|
return;
|
|
}
|
|
|
|
var link = Link(publicTerm: title, filePath: url, alt: alt);
|
|
links.add(link);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static bool isExternalLink(String url) {
|
|
return url.startsWith(RegExp(r'[A-Za-z]{2,5}:\/\/'));
|
|
}
|
|
|
|
List<Link> visit(List<md.Node> nodes) {
|
|
for (final node in nodes) {
|
|
node.accept(this);
|
|
}
|
|
return links;
|
|
}
|
|
|
|
String _getText(List<md.Node>? nodes) {
|
|
if (nodes == null) {
|
|
return "";
|
|
}
|
|
|
|
var text = "";
|
|
for (final node in nodes) {
|
|
if (node is md.Text) {
|
|
text += node.text;
|
|
} else if (node is md.Element) {
|
|
text += _getText(node.children);
|
|
}
|
|
}
|
|
|
|
return text;
|
|
}
|
|
}
|
|
|
|
/// Parse [[term]]
|
|
class WikiLinkSyntax extends md.InlineSyntax {
|
|
static final String _pattern = r'\[\[([^\[\]]+)\]\]';
|
|
|
|
// In Obsidian style, the link is like [[fileToLinkTo|display text]]
|
|
final bool obsidianStyle;
|
|
|
|
WikiLinkSyntax({this.obsidianStyle = true}) : super(_pattern);
|
|
|
|
@override
|
|
bool onMatch(md.InlineParser parser, Match match) {
|
|
var term = match[1]!.trim();
|
|
var displayText = term;
|
|
|
|
if (term.contains('|')) {
|
|
var s = term.split('|');
|
|
if (obsidianStyle) {
|
|
term = s[0].trimRight();
|
|
displayText = s[1].trimLeft();
|
|
} else {
|
|
displayText = s[0].trimRight();
|
|
term = s[1].trimLeft();
|
|
}
|
|
}
|
|
|
|
var el = md.Element('a', [md.Text(displayText)]);
|
|
el.attributes['type'] = 'wiki';
|
|
el.attributes['href'] = '[[$term]]';
|
|
el.attributes['term'] = term;
|
|
|
|
parser.addNode(el);
|
|
return true;
|
|
}
|
|
}
|