pull/989/head
Zef Hemel 2024-07-25 15:18:58 +02:00
parent cff343a66b
commit 3ca132e16a
3 changed files with 63 additions and 13 deletions

View File

@ -9,6 +9,7 @@ import { listFilesCached } from "../federation/federation.ts";
import { queryObjects } from "../index/plug_api.ts"; import { queryObjects } from "../index/plug_api.ts";
import { folderName } from "$sb/lib/resolve.ts"; import { folderName } from "$sb/lib/resolve.ts";
import { decoration } from "$sb/syscalls.ts"; import { decoration } from "$sb/syscalls.ts";
import type { LinkObject } from "../index/page_links.ts";
// A meta page is a page tagged with either #template or #meta // A meta page is a page tagged with either #template or #meta
const isMetaPageFilter: QueryExpression = ["or", ["=", ["attr", "tags"], [ const isMetaPageFilter: QueryExpression = ["or", ["=", ["attr", "tags"], [
@ -66,14 +67,34 @@ export async function pageComplete(completeEvent: CompleteEvent) {
// Include both pages and meta in page completion in ```include and ```template blocks // Include both pages and meta in page completion in ```include and ```template blocks
allPages = await queryObjects<PageMeta>("page", {}, 5); allPages = await queryObjects<PageMeta>("page", {}, 5);
} else { } else {
// Otherwise, just complete non-meta pages // This is the most common case, we're combining three types of completions here:
allPages = await queryObjects<PageMeta>("page", { allPages = (await Promise.all([
filter: ["not", isMetaPageFilter], // All non-meta pages
}, 5); queryObjects<PageMeta>("page", {
// and attachments filter: ["not", isMetaPageFilter],
allPages = allPages.concat( }, 5),
await queryObjects<AttachmentMeta>("attachment", {}, 5), // All attachments
); queryObjects<AttachmentMeta>("attachment", {}, 5),
// And all links to non-existing pages (to augment the existing ones)
queryObjects<LinkObject>("link", {
filter: ["and", ["attr", "toPage"], ["not", ["call", "pageExists", [[
"attr",
"toPage",
]]]]],
select: [{ name: "toPage" }],
}, 5).then((brokenLinks) =>
// Rewrite them to PageMeta shaped objects
brokenLinks.map((link): PageMeta => ({
ref: link.toPage!,
tag: "page",
tags: ["non-existing"], // Picked up later in completion
name: link.toPage!,
created: "",
lastModified: "",
perm: "rw",
}))
),
])).flat();
} }
// Don't complete hidden pages // Don't complete hidden pages
@ -143,6 +164,9 @@ export async function pageComplete(completeEvent: CompleteEvent) {
label: pageMeta.name, label: pageMeta.name,
displayLabel: decoratedName, displayLabel: decoratedName,
boost: new Date(pageMeta.lastModified).getTime(), boost: new Date(pageMeta.lastModified).getTime(),
detail: pageMeta.tags?.includes("non-existing")
? "Linked but not created"
: undefined,
type: "page", type: "page",
}); });
} else { } else {

View File

@ -62,6 +62,7 @@ import { LimitedMap } from "$lib/limited_map.ts";
import { plugPrefix } from "$common/spaces/constants.ts"; import { plugPrefix } from "$common/spaces/constants.ts";
import { lezerToParseTree } from "$common/markdown_parser/parse_tree.ts"; import { lezerToParseTree } from "$common/markdown_parser/parse_tree.ts";
import { findNodeMatching } from "$sb/lib/tree.ts"; import { findNodeMatching } from "$sb/lib/tree.ts";
import type { LinkObject } from "../plugs/index/page_links.ts";
const frontMatterRegex = /^---\n(([^\n]|\n)*?)---\n/; const frontMatterRegex = /^---\n(([^\n]|\n)*?)---\n/;
@ -787,9 +788,27 @@ export class Client {
return; return;
} }
const allPages = await this.clientSystem.queryObjects<PageMeta>("page", {}); const allPages = await this.clientSystem.queryObjects<PageMeta>("page", {});
const allBrokenLinkPages = (await this.clientSystem.queryObjects<
LinkObject
>("link", {
filter: ["and", ["attr", "toPage"], ["not", ["call", "pageExists", [[
"attr",
"toPage",
]]]]],
select: [{ name: "toPage" }],
})).map((link): PageMeta => ({
ref: link.toPage!,
tag: "page",
_isBrokenLink: true,
name: link.toPage!,
created: "",
lastModified: "",
perm: "rw",
}));
this.ui.viewDispatch({ this.ui.viewDispatch({
type: "update-page-list", type: "update-page-list",
allPages, allPages: allPages.concat(allBrokenLinkPages),
}); });
} }

View File

@ -65,6 +65,7 @@ export function PageNavigator({
name: (pageMeta.pageDecoration?.prefix ?? "") + pageMeta.name, name: (pageMeta.pageDecoration?.prefix ?? "") + pageMeta.name,
description, description,
orderId: orderId, orderId: orderId,
hint: pageMeta._isBrokenLink ? "Create page" : undefined,
}); });
} else if (mode === "meta") { } else if (mode === "meta") {
// Special behavior for #template and #meta pages // Special behavior for #template and #meta pages
@ -103,7 +104,11 @@ export function PageNavigator({
const pageNoun = mode === "meta" ? mode : "page"; const pageNoun = mode === "meta" ? mode : "page";
return ( return (
<FilterList <FilterList
placeholder={mode === "page" ? "Page" : (mode === "meta" ? "#template or #meta page" : "Any page, also hidden")} placeholder={mode === "page"
? "Page"
: (mode === "meta"
? "#template or #meta page"
: "Any page, also hidden")}
label="Open" label="Open"
options={options} options={options}
vimMode={vimMode} vimMode={vimMode}
@ -116,7 +121,7 @@ export function PageNavigator({
onKeyPress={(key, text) => { onKeyPress={(key, text) => {
// Pages cannot start with ^, as documented in Page Name Rules // Pages cannot start with ^, as documented in Page Name Rules
if (key === "^" && text === "^") { if (key === "^" && text === "^") {
switch(mode) { switch (mode) {
case "page": case "page":
onModeSwitch("meta"); onModeSwitch("meta");
break; break;
@ -159,7 +164,9 @@ export function PageNavigator({
if (mode !== "all") { if (mode !== "all") {
// Filter out hidden pages // Filter out hidden pages
options = options.filter((page) => !(page.pageDecoration?.hide === true)); options = options.filter((page) =>
!(page.pageDecoration?.hide === true)
);
} }
return options; return options;
}} }}