From af69dd6e4879080a04655d8319235410c08c7e2a Mon Sep 17 00:00:00 2001 From: Vishesh Handa Date: Tue, 26 May 2020 14:49:01 +0200 Subject: [PATCH] Note: Allow returning the links in the document This isn't perfect as we aren't handling referenced links and maybe even autolinks, but it's a start. Related to #141 --- lib/core/note.dart | 89 +++++++++++++++++++++++++++++++++++++++++++++ test/note_test.dart | 27 ++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/lib/core/note.dart b/lib/core/note.dart index c9f5b90b..a1166c47 100644 --- a/lib/core/note.dart +++ b/lib/core/note.dart @@ -9,6 +9,9 @@ import 'package:gitjournal/utils/logger.dart'; import 'package:gitjournal/utils/datetime.dart'; import 'package:path/path.dart' as p; +import 'package:meta/meta.dart'; + +import 'package:markdown/markdown.dart' as md; import 'md_yaml_doc.dart'; import 'md_yaml_doc_codec.dart'; @@ -366,6 +369,36 @@ class Note with NotesNotifier { return date.toString(); } + + Future> fetchLinks() async { + final doc = md.Document( + encodeHtml: false, + extensionSet: md.ExtensionSet.gitHubFlavored, + ); + + var lines = body.replaceAll('\r\n', '\n').split('\n'); + var nodes = doc.parseLines(lines); + var possibleLinks = LinkExtractor().visit(nodes); + + var links = []; + for (var l in possibleLinks) { + var path = l.filePath; + var isLocal = (path.startsWith('/') || path.startsWith('.')) && + !path.contains('://'); + if (isLocal) { + l.filePath = p.join(parent.folderPath, p.normalize(l.filePath)); + links.add(l); + } + } + + doc.linkReferences.forEach((key, value) { + print(value); + var filePath = value.destination; + links.add(Link(term: key, filePath: filePath)); + }); + + return links; + } } String buildTitleFileName(String parentDir, String title) { @@ -388,3 +421,59 @@ String buildTitleFileName(String parentDir, String title) { } } } + +class Link { + String term; + String filePath; + + Link({@required this.term, @required this.filePath}); + + @override + int get hashCode => filePath.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Note && + runtimeType == other.runtimeType && + filePath == other.filePath; +} + +class LinkExtractor implements md.NodeVisitor { + List links = []; + + @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 title = el.attributes['title'] ?? ""; + if (title.isEmpty) { + for (var child in el.children) { + if (child is md.Text) { + title += child.text; + } + } + } + + var url = el.attributes['href']; + var link = Link(term: title, filePath: url); + links.add(link); + } + } + + List visit(List nodes) { + for (final node in nodes) { + node.accept(this); + } + return links; + } +} diff --git a/test/note_test.dart b/test/note_test.dart index 0e5ab3f9..73e5d2b3 100644 --- a/test/note_test.dart +++ b/test/note_test.dart @@ -112,5 +112,32 @@ Hello"""; var actualContent = File(notePath).readAsStringSync(); expect(actualContent, equals(expectedContent)); }); + + test('Should parse links', () async { + var content = """--- +title: Foo +--- + +[Hi](./foo.md) +[Hi2](./po/../food.md) +[Web](http://example.com) +"""; + + var notePath = p.join(tempDir.path, "note6.md"); + File(notePath).writeAsString(content); + + var parentFolder = NotesFolderFS(null, tempDir.path); + var note = Note(parentFolder, notePath); + await note.load(); + + var links = await note.fetchLinks(); + expect(links[0].filePath, p.join(tempDir.path, "foo.md")); + expect(links[0].term, "Hi"); + + expect(links[1].filePath, p.join(tempDir.path, "food.md")); + expect(links[1].term, "Hi2"); + + expect(links.length, 2); + }); }); }