Skip to content

Commit 40895ad

Browse files
committed
Enhance link indexing to support additional link types
* New "type" attribute for links (page, file, url) * Now indexes external links as well
1 parent 66807fa commit 40895ad

File tree

2 files changed

+56
-40
lines changed

2 files changed

+56
-40
lines changed

plugs/index/link.test.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const testPage = `
99
---
1010
attribute: "[[fm-link]]"
1111
---
12-
This is a [[page-link]] to [[aliased-link|aliased]], or [this](md-link), and [[broken]]
12+
This is a [[page-link]] to [[aliased-link|aliased]], or [this](md-link), and [[broken]], or [external](https://example.com), or [document](test.jpg), or [[test2.jpg]]
1313
`.trim();
1414

1515
Deno.test("Test page link indexing", async () => {
@@ -34,11 +34,22 @@ Deno.test("Test page link indexing", async () => {
3434
};
3535

3636
const objects = await indexLinks(pageMeta, frontmatter, tree, testPage);
37-
// console.log(objects);
38-
assertEquals(objects.length, 6); // 5 links + 1 aspiring page
37+
assertEquals(objects.length, 9); // 7 links + 1 aspiring page
3938
assertEquals(objects[0].toPage, "fm-link");
39+
assertEquals(objects[0].type, "page");
4040
assertEquals(objects[1].toPage, "page-link");
41+
assertEquals(objects[1].type, "page");
4142
assertEquals(objects[2].toPage, "aliased-link");
43+
assertEquals(objects[2].type, "page");
4244
assertEquals(objects[2].alias, "aliased");
4345
assertEquals(objects[3].toPage, "folder/md-link"); // relative
46+
assertEquals(objects[3].type, "page");
47+
assertEquals(objects[4].toPage, "broken");
48+
assertEquals(objects[4].type, "page");
49+
assertEquals(objects[5].toURL, "https://example.com");
50+
assertEquals(objects[5].type, "url");
51+
assertEquals(objects[6].toFile, "folder/test.jpg");
52+
assertEquals(objects[6].type, "file");
53+
assertEquals(objects[7].toFile, "test2.jpg");
54+
assertEquals(objects[7].type, "file");
4455
});

plugs/index/link.ts

Lines changed: 42 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -27,26 +27,21 @@ import type {
2727
} from "@silverbulletmd/silverbullet/type/index";
2828
import { extractSnippet } from "./snippet.ts";
2929

30-
export type LinkObject = ObjectValue<
31-
& {
32-
// Common to all links
33-
page: string;
34-
pos: number;
35-
snippet: string;
36-
alias?: string;
37-
pageLastModified: string;
38-
}
39-
& ({
40-
// Page Link
41-
toPage: string;
42-
toFile?: never;
43-
} | {
44-
// Document Link
45-
toFile: string;
46-
// The page the link occurs in
47-
toPage?: never;
48-
})
49-
>;
30+
export type LinkObject = ObjectValue<{
31+
// Common to all links
32+
page: string;
33+
pos: number;
34+
type: "page" | "file" | "url";
35+
snippet: string;
36+
alias?: string;
37+
pageLastModified: string;
38+
// Page Link
39+
toPage?: string;
40+
// File Link
41+
toFile?: string;
42+
// External URL
43+
toURL?: string;
44+
}>;
5045

5146
/**
5247
* Represents a page that does not yet exist, but is being linked to. A page "aspiring" to be created.
@@ -84,8 +79,9 @@ export async function indexLinks(
8479
const url = wikiLinkPage.children![0].text!;
8580
const pos = wikiLinkPage.from!;
8681

87-
const link: Partial<LinkObject> = {
82+
const link: LinkObject = {
8883
ref: `${name}@${pos}`,
84+
type: "page", // can be swapped later
8985
tag: "link",
9086
snippet: extractSnippet(name, pageText, pos),
9187
pos,
@@ -100,8 +96,10 @@ export async function indexLinks(
10096
return true;
10197
} else if (isMarkdownPath(ref.path)) {
10298
link.toPage = getNameFromPath(ref.path);
99+
link.type = "page";
103100
} else {
104101
link.toFile = ref.path;
102+
link.type = "file";
105103
}
106104

107105
if (wikiLinkAlias) {
@@ -112,44 +110,48 @@ export async function indexLinks(
112110
return true;
113111
}
114112

115-
// Also index [Markdown style]() links
113+
// Index [markdown style]() links
116114
if (n.type === "Link" || n.type === "Image") {
117115
// The [[Wiki links]] also have a wrapping Image node, but this just fails at the regex
118116
mdLinkRegex.lastIndex = 0;
119117
const match = mdLinkRegex.exec(renderToText(n));
120118
if (!match) {
121119
return false;
122120
}
123-
let { title: alias, url } = match.groups as {
121+
const { title: alias, url } = match.groups as {
124122
url: string;
125123
title: string;
126124
};
127125

128126
// Check if local link
129-
if (!isLocalURL(url)) {
130-
return false;
131-
}
132127
const pos = n.from!;
133-
url = resolveMarkdownLink(name, decodeURI(url));
134-
135-
const link: Partial<LinkObject> = {
128+
const link: LinkObject = {
136129
ref: `${name}@${pos}`,
137130
tag: "link",
131+
type: "page", // swapped out later if needed
138132
snippet: extractSnippet(name, pageText, pos),
139133
pos,
140134
range: [n.from!, n.to!],
141135
page: name,
142136
pageLastModified: pageMeta.lastModified,
143137
};
144138

145-
const ref = parseToRef(url);
146-
if (!ref) {
147-
// Invalid links aren't indexed
148-
return true;
149-
} else if (isMarkdownPath(ref.path)) {
150-
link.toPage = getNameFromPath(ref.path);
139+
if (isLocalURL(url)) {
140+
const ref = parseToRef(resolveMarkdownLink(name, decodeURI(url)));
141+
if (!ref) {
142+
// Invalid links aren't indexed
143+
return true;
144+
} else if (isMarkdownPath(ref.path)) {
145+
link.toPage = getNameFromPath(ref.path);
146+
link.type = "page";
147+
} else {
148+
link.toFile = ref.path;
149+
link.type = "file";
150+
}
151151
} else {
152-
link.toFile = ref.path;
152+
// External URL
153+
link.type = "url";
154+
link.toURL = url;
153155
}
154156

155157
if (alias) {
@@ -174,9 +176,10 @@ export async function indexLinks(
174176
if (match && match.groups && match[0] === trimmed) {
175177
const { leadingTrivia, stringRef, alias } = match.groups;
176178
const pos = textNode.from! + match.index! + leadingTrivia.length;
177-
const link: Partial<LinkObject> = {
179+
const link: LinkObject = {
178180
ref: `${name}@${pos}`,
179181
tag: "link",
182+
type: "page", // final value set later
180183
page: name,
181184
snippet: extractSnippet(name, pageText, pos),
182185
pos: pos,
@@ -193,8 +196,10 @@ export async function indexLinks(
193196
return true;
194197
} else if (isMarkdownPath(ref.path)) {
195198
link.toPage = getNameFromPath(ref.path);
199+
link.type = "page";
196200
} else {
197201
link.toFile = ref.path;
202+
link.type = "file";
198203
}
199204

200205
if (alias) {

0 commit comments

Comments
 (0)