import 'dart:isolate'; import 'package:flutter/material.dart'; import 'package:markdown/markdown.dart' as md; import 'package:path/path.dart' as p; import 'package:synchronized/synchronized.dart'; import 'package:gitjournal/core/link.dart'; class LinksLoader { Isolate? _isolate; ReceivePort _receivePort = ReceivePort(); SendPort? _sendPort; var _loadingLock = Lock(); Future _initIsolate() async { if (_isolate != null && _sendPort != null) return; return await _loadingLock.synchronized(() async { if (_isolate != null && _sendPort != null) return; if (_isolate != null) { _isolate!.kill(priority: Isolate.immediate); _isolate = null; } _isolate = await Isolate.spawn(_isolateMain, _receivePort.sendPort); var data = await _receivePort.first; assert(data is SendPort); _sendPort = data as SendPort; }); } Future> parseLinks( {required String body, required String filePath}) async { await _initIsolate(); var rec = ReceivePort(); _sendPort!.send(_LoadingMessage(body, filePath, rec.sendPort)); var data = await rec.first; assert(data is List); return data; } } class _LoadingMessage { String body; String filePath; SendPort sendPort; _LoadingMessage(this.body, this.filePath, this.sendPort); } void _isolateMain(SendPort toMainSender) { ReceivePort fromMainRec = ReceivePort(); toMainSender.send(fromMainRec.sendPort); fromMainRec.listen((data) async { assert(data is _LoadingMessage); var msg = data as _LoadingMessage; var links = parseLinks(msg.body, msg.filePath); msg.sendPort.send(links); }); } @visibleForTesting List parseLinks(String body, String filePath) { var parentFolderPath = p.dirname(filePath); final doc = md.Document( encodeHtml: false, extensionSet: md.ExtensionSet.gitHubFlavored, inlineSyntaxes: [WikiLinkSyntax()], ); var lines = body.replaceAll('\r\n', '\n').split('\n'); var nodes = doc.parseLines(lines); var possibleLinks = LinkExtractor(filePath).visit(nodes); var links = []; for (var l in possibleLinks) { if (l.isWikiLink) { links.add(l); continue; } l.filePath = p.join(parentFolderPath, p.normalize(l.filePath!)); links.add(l); } doc.linkReferences.forEach((key, value) { var path = value.destination; if (LinkExtractor.isExternalLink(path)) { return; } var l = Link(publicTerm: value.label, filePath: "", alt: value.title); if (path.startsWith('#') || path.startsWith('//')) { l.headingID = path; l.filePath = filePath; } else { l.filePath = p.normalize(p.join(parentFolderPath, path)); } links.add(l); }); return links; }