2024-05-28 02:33:41 +08:00
|
|
|
import { ClickEvent } from "$sb/types.ts";
|
2024-03-16 22:29:24 +08:00
|
|
|
import { syntaxTree } from "@codemirror/language";
|
|
|
|
import { Decoration } from "@codemirror/view";
|
2023-07-14 22:56:20 +08:00
|
|
|
import { Client } from "../client.ts";
|
2024-05-28 02:33:41 +08:00
|
|
|
import { decoratorStateField, isCursorInRange, LinkWidget } from "./util.ts";
|
2023-07-30 05:41:37 +08:00
|
|
|
import { resolvePath } from "$sb/lib/resolve.ts";
|
2024-05-28 02:33:41 +08:00
|
|
|
import { encodePageRef, parsePageRef } from "$sb/lib/page_ref.ts";
|
2022-11-18 23:04:37 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Plugin to hide path prefix when the cursor is not inside.
|
|
|
|
*/
|
2023-12-22 22:55:50 +08:00
|
|
|
export function cleanWikiLinkPlugin(client: Client) {
|
2022-12-09 23:09:53 +08:00
|
|
|
return decoratorStateField((state) => {
|
|
|
|
const widgets: any[] = [];
|
|
|
|
// let parentRange: [number, number];
|
2024-05-28 02:33:41 +08:00
|
|
|
const allKnownFiles = client.clientSystem.allKnownFiles;
|
2022-12-09 23:09:53 +08:00
|
|
|
syntaxTree(state).iterate({
|
|
|
|
enter: ({ type, from, to }) => {
|
|
|
|
if (type.name !== "WikiLink") {
|
|
|
|
return;
|
2022-11-28 23:42:54 +08:00
|
|
|
}
|
2022-12-09 23:09:53 +08:00
|
|
|
const text = state.sliceDoc(from, to);
|
2024-05-28 02:33:41 +08:00
|
|
|
const match = /(!?\[\[)([^\]\|]+)(?:\|([^\]]+))?(\]\])/g.exec(text);
|
|
|
|
|
2022-12-09 23:09:53 +08:00
|
|
|
if (!match) return;
|
2024-05-28 02:33:41 +08:00
|
|
|
const [_fullMatch, firstMark, url, alias, lastMark] = match;
|
|
|
|
|
|
|
|
if (firstMark.startsWith("!")) {
|
|
|
|
// Is inline image
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let fileExists = !client.fullSyncCompleted;
|
2022-11-30 18:26:47 +08:00
|
|
|
|
2024-05-28 02:33:41 +08:00
|
|
|
const pageRef = parsePageRef(url);
|
|
|
|
pageRef.page = resolvePath(client.currentPage, "/" + pageRef.page);
|
2024-01-24 21:44:39 +08:00
|
|
|
const lowerCasePageName = pageRef.page.toLowerCase();
|
2024-05-28 02:33:41 +08:00
|
|
|
|
|
|
|
for (const fileName of allKnownFiles) {
|
|
|
|
if (
|
|
|
|
fileName.toLowerCase().replace(/\.md$/, "") === lowerCasePageName
|
|
|
|
) {
|
|
|
|
fileExists = true;
|
2022-12-09 23:09:53 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-07-12 17:06:59 +08:00
|
|
|
if (
|
2024-01-24 21:44:39 +08:00
|
|
|
pageRef.page === "" ||
|
|
|
|
client.plugSpaceRemotePrimitives.isLikelyHandled(pageRef.page)
|
2023-07-12 17:06:59 +08:00
|
|
|
) {
|
|
|
|
// Empty page name with local @anchor use or a link to a page that dynamically generated by a plug
|
2024-05-28 02:33:41 +08:00
|
|
|
fileExists = true;
|
2022-12-09 23:09:53 +08:00
|
|
|
}
|
2022-11-30 18:26:47 +08:00
|
|
|
|
2022-12-09 23:09:53 +08:00
|
|
|
if (isCursorInRange(state, [from, to])) {
|
|
|
|
// Only attach a CSS class, then get out
|
2024-05-28 02:33:41 +08:00
|
|
|
if (!fileExists) {
|
2022-11-27 15:48:01 +08:00
|
|
|
widgets.push(
|
2022-12-09 23:09:53 +08:00
|
|
|
Decoration.mark({
|
|
|
|
class: "sb-wiki-link-page-missing",
|
2024-05-28 02:33:41 +08:00
|
|
|
}).range(
|
|
|
|
from + firstMark.length,
|
|
|
|
to - lastMark.length,
|
|
|
|
),
|
2022-11-27 15:48:01 +08:00
|
|
|
);
|
2022-12-09 23:09:53 +08:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2022-11-28 23:42:54 +08:00
|
|
|
|
2024-05-28 02:33:41 +08:00
|
|
|
const linkText = alias ||
|
|
|
|
(url.includes("/") ? url.split("/").pop()! : url);
|
2022-12-09 23:09:53 +08:00
|
|
|
|
|
|
|
// And replace it with a widget
|
|
|
|
widgets.push(
|
2024-05-28 02:33:41 +08:00
|
|
|
Decoration.replace({
|
2022-12-09 23:09:53 +08:00
|
|
|
widget: new LinkWidget(
|
|
|
|
{
|
|
|
|
text: linkText,
|
2024-05-28 02:33:41 +08:00
|
|
|
title: fileExists
|
2024-01-24 21:44:39 +08:00
|
|
|
? `Navigate to ${encodePageRef(pageRef)}`
|
|
|
|
: `Create ${pageRef.page}`,
|
|
|
|
href: `/${encodePageRef(pageRef)}`,
|
2024-05-28 02:33:41 +08:00
|
|
|
cssClass: fileExists
|
2022-12-09 23:09:53 +08:00
|
|
|
? "sb-wiki-link-page"
|
|
|
|
: "sb-wiki-link-page-missing",
|
|
|
|
callback: (e) => {
|
|
|
|
if (e.altKey) {
|
|
|
|
// Move cursor into the link
|
2024-07-04 01:13:54 +08:00
|
|
|
client.editorView.dispatch({
|
2024-05-28 02:33:41 +08:00
|
|
|
selection: { anchor: from + firstMark.length },
|
2022-12-09 23:09:53 +08:00
|
|
|
});
|
2024-07-04 01:13:54 +08:00
|
|
|
client.focus();
|
|
|
|
return;
|
2022-12-09 23:09:53 +08:00
|
|
|
}
|
|
|
|
// Dispatch click event to navigate there without moving the cursor
|
|
|
|
const clickEvent: ClickEvent = {
|
2024-01-24 21:44:39 +08:00
|
|
|
page: client.currentPage,
|
2022-12-09 23:09:53 +08:00
|
|
|
ctrlKey: e.ctrlKey,
|
|
|
|
metaKey: e.metaKey,
|
|
|
|
altKey: e.altKey,
|
|
|
|
pos: from,
|
|
|
|
};
|
2023-12-22 22:55:50 +08:00
|
|
|
client.dispatchAppEvent("page:click", clickEvent).catch(
|
2022-12-09 23:09:53 +08:00
|
|
|
console.error,
|
|
|
|
);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
2024-05-28 02:33:41 +08:00
|
|
|
}).range(from, to),
|
2022-12-09 23:09:53 +08:00
|
|
|
);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
return Decoration.set(widgets, true);
|
|
|
|
});
|
2022-11-18 23:04:37 +08:00
|
|
|
}
|