39 | {/*
Search your external links
*/}
40 |
41 |
49 |
50 |
51 | {result.length} found
52 |
53 |
75 |
76 | );
77 | };
78 |
--------------------------------------------------------------------------------
/src/extlnklib.ts:
--------------------------------------------------------------------------------
1 | import { Plugin, TFile } from "obsidian";
2 | import DanpungPlugin from "./main";
3 | import { parse } from 'node-html-parser';
4 |
5 | export {
6 | LinkIndexer, Link, LinkType, scanMarkdownLinks, scanHtmlLinks, scanOrphanedLinks
7 | }
8 |
9 | enum LinkType {
10 | FQL = 'FQL',
11 | FQLRelative = 'FQLRelative',
12 | Unknown = 'Unknown'
13 | }
14 |
15 | class Link {
16 | constructor(
17 | public Text: string,
18 | public Path: string,
19 | public FilePath: string,
20 | public Tags: string[] = []) { }
21 | }
22 |
23 | class LinkIndexer {
24 |
25 | constructor(
26 | public plugin: DanpungPlugin,
27 | public linkStore: Link[] = []) {
28 | plugin.registerEvent(
29 | plugin.app.vault.on('modify', (file) => {
30 | // Read the file and update the link indices
31 | plugin.app.vault.cachedRead(file as TFile).then((content) => {
32 | let mode = LinkType.FQL;
33 | if (!plugin.settings.onlyFullLink) {
34 | mode = LinkType.FQLRelative;
35 | }
36 | const scanedLinks = scanMarkdownLinks(content, file.path, mode);
37 | if (plugin.settings.htmlAnchorLinks) {
38 | scanedLinks.push(...scanHtmlLinks(content, file.path, mode));
39 | }
40 | if (plugin.settings.urlLinks) {
41 | scanedLinks.push(...scanOrphanedLinks(content, file.path));
42 | }
43 |
44 | this.updateStore(file.path, scanedLinks);
45 | })
46 | })
47 | )
48 | plugin.registerEvent(
49 | plugin.app.vault.on('rename', (file, oldPath) => {
50 | plugin.app.vault.cachedRead(file as TFile).then((content) => {
51 | let mode = LinkType.FQL;
52 | if (!plugin.settings.onlyFullLink) {
53 | mode = LinkType.FQLRelative;
54 | }
55 | const scanedLinks = scanMarkdownLinks(content, file.path, mode);
56 | if (plugin.settings.htmlAnchorLinks) {
57 | scanedLinks.push(...scanHtmlLinks(content, file.path, mode));
58 | }
59 | if (plugin.settings.urlLinks) {
60 | scanedLinks.push(...scanOrphanedLinks(content, file.path));
61 | }
62 |
63 | this.updateStore(oldPath, scanedLinks);
64 | })
65 | })
66 | )
67 | plugin.registerEvent(
68 | plugin.app.vault.on('delete', (file) => {
69 | console.log('File deleted: ' + file.path);
70 | this.updateStore(file.path, []);
71 | })
72 | )
73 | plugin.registerEvent(
74 | plugin.app.metadataCache.on('changed', (file, data, cache) => {
75 | const updatedTags = cache.tags?.map((tag) => tag.tag) ?? [];
76 | updatedTags.push(...(cache.frontmatter?.tags ?? []));
77 | this.updateTags(file.path, updatedTags);
78 | }
79 | ))
80 | }
81 |
82 | updateStore = (filepath: string, links: Link[]) => {
83 | this.linkStore = this.linkStore.filter((link) => link.FilePath !== filepath)
84 | this.linkStore.push(...links);
85 |
86 | // Update settings.linkStore
87 | this.plugin.settings.linkStore = this.linkStore;
88 | // Update status bar
89 | this.plugin.statusBarItemEl?.setText(`External Links: ${this.linkStore.length}`);
90 | }
91 |
92 | updateTags = (filepath: string, tags: string[]) => {
93 | this.linkStore.map((link) => {
94 | if (link.FilePath === filepath) {
95 | link.Tags = tags;
96 | }
97 | return link;
98 | })
99 |
100 | // Update settings.linkStore
101 | this.plugin.settings.linkStore = this.linkStore;
102 | }
103 | }
104 |
105 | const regexMdLinks = /\[([^\[]+)\](\(.*\))/gm
106 |
107 | const fullLinkOnlyRegex = /^\[([\w\s\d]+)\]\((https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*))\)$/
108 |
109 | const fullAndRelative = /\[([^\[]+)\]\((.*)\)/
110 |
111 | const scanMarkdownLinks = (content: string, filepath: string, mode: LinkType = LinkType.FQL): Link[] => {
112 |
113 | const links: Link[] = [];
114 |
115 | const mdLinks = content.matchAll(regexMdLinks);
116 |
117 | let pattern = fullLinkOnlyRegex;
118 | if (mode != LinkType.FQL) {
119 | pattern = fullAndRelative;
120 | }
121 |
122 | for (const mdLink of mdLinks) {
123 | const linkExpr = mdLink[0];
124 | var matched = linkExpr.match(pattern);
125 | if (matched)
126 | links.push(new Link(matched[1], matched[2], filepath));
127 | }
128 |
129 | return links;
130 | }
131 |
132 | const fullHtmlLinkRegex = /