From 3350c7f076b1480476fca24f8ced46cf7b996c65 Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Fri, 22 Dec 2023 15:55:50 +0100 Subject: [PATCH] More instant page navigator --- plug-api/lib/frontmatter.ts | 5 ---- plugs/editor/complete.ts | 16 ++++++------- server/server_system.ts | 2 +- web/client.ts | 46 +++++++++++++++++++++++++++++++++---- web/cm_plugins/wiki_link.ts | 19 ++++++++------- web/editor_state.ts | 4 ++-- web/editor_ui.tsx | 4 ++-- web/reducer.ts | 12 +++++++--- web/space.ts | 26 ++++----------------- web/sync_service.ts | 2 +- web/types.ts | 3 ++- 11 files changed, 79 insertions(+), 60 deletions(-) diff --git a/plug-api/lib/frontmatter.ts b/plug-api/lib/frontmatter.ts index 80d98f1..ecb1dee 100644 --- a/plug-api/lib/frontmatter.ts +++ b/plug-api/lib/frontmatter.ts @@ -97,11 +97,6 @@ export async function extractFrontmatter( return undefined; }); - if (data.name) { - data.displayName = data.name; - delete data.name; - } - return data; } diff --git a/plugs/editor/complete.ts b/plugs/editor/complete.ts index 5b2ceaf..cd330cd 100644 --- a/plugs/editor/complete.ts +++ b/plugs/editor/complete.ts @@ -46,31 +46,31 @@ export async function pageComplete(completeEvent: CompleteEvent) { const completions: any[] = []; if (pageMeta.displayName) { completions.push({ - label: pageMeta.displayName, - boost: pageMeta.lastModified, + label: `${pageMeta.displayName}`, + boost: new Date(pageMeta.lastModified).getTime(), apply: isInTemplateContext ? pageMeta.name : `${pageMeta.name}|${pageMeta.displayName}`, - detail: "alias", + detail: `displayName: ${pageMeta.name}`, type: "page", }); } if (Array.isArray(pageMeta.aliases)) { for (const alias of pageMeta.aliases) { completions.push({ - label: alias, - boost: pageMeta.lastModified, + label: `${alias}`, + boost: new Date(pageMeta.lastModified).getTime(), apply: isInTemplateContext ? pageMeta.name : `${pageMeta.name}|${alias}`, - detail: "alias", + detail: `alias: ${pageMeta.name}`, type: "page", }); } } completions.push({ - label: pageMeta.name, - boost: pageMeta.lastModified, + label: `${pageMeta.name}`, + boost: new Date(pageMeta.lastModified).getTime(), type: "page", }); return completions; diff --git a/server/server_system.ts b/server/server_system.ts index e20bcd4..1a0a2c8 100644 --- a/server/server_system.ts +++ b/server/server_system.ts @@ -137,7 +137,7 @@ export class ServerSystem { ); this.listInterval = setInterval(() => { - space.updatePageListCache().catch(console.error); + space.updatePageList().catch(console.error); }, fileListInterval); eventHook.addLocalListener("file:changed", (path, localChange) => { diff --git a/web/client.ts b/web/client.ts index 48ce45d..5f40c64 100644 --- a/web/client.ts +++ b/web/client.ts @@ -107,6 +107,9 @@ export class Client { spaceDataStore!: DataStore; mq!: DataStoreMQ; + // Used by the "wiki link" highlighter to check if a page exists + public allKnownPages = new Set(); + constructor( private parent: Element, public syncMode = false, @@ -226,6 +229,8 @@ export class Client { console.error("Interval sync error", e); } }, pageSyncInterval); + + this.updatePageListCache().catch(console.error); } private initSync() { @@ -235,7 +240,7 @@ export class Client { // console.log("Operations", operations); if (operations > 0) { // Update the page list - await this.space.updatePageListCache(); + await this.space.updatePageList(); } if (operations !== undefined) { // "sync:success" is called with a number of operations only from syncSpace(), not from syncing individual pages @@ -499,6 +504,28 @@ export class Client { }, ); + // Caching a list of known pages for the wiki_link highlighter (that checks if a page exists) + this.eventHook.addLocalListener("page:saved", (pageName: string) => { + // Make sure this page is in the list of known pages + this.allKnownPages.add(pageName); + }); + + this.eventHook.addLocalListener("page:deleted", (pageName: string) => { + this.allKnownPages.delete(pageName); + }); + + this.eventHook.addLocalListener( + "file:listed", + (allFiles: FileMeta[]) => { + // Update list of known pages + this.allKnownPages = new Set( + allFiles.filter((f) => f.name.endsWith(".md")).map((f) => + f.name.slice(0, -3) + ), + ); + }, + ); + this.space.watch(); return localSpacePrimitives; @@ -582,11 +609,19 @@ export class Client { ); } - async startPageNavigate() { - // Fetch all pages from the index - const pages = await this.system.queryObjects("page", {}); + startPageNavigate() { // Then show the page navigator - this.ui.viewDispatch({ type: "start-navigate", pages }); + this.ui.viewDispatch({ type: "start-navigate" }); + this.updatePageListCache().catch(console.error); + } + + async updatePageListCache() { + console.log("Updating page list cache"); + const allPages = await this.system.queryObjects("page", {}); + this.ui.viewDispatch({ + type: "update-page-list", + allPages, + }); } private progressTimeout?: number; @@ -744,6 +779,7 @@ export class Client { actualResult = result; } } + // console.log("Compeltion result", actualResult); return actualResult; } diff --git a/web/cm_plugins/wiki_link.ts b/web/cm_plugins/wiki_link.ts index edce0d6..352c0b7 100644 --- a/web/cm_plugins/wiki_link.ts +++ b/web/cm_plugins/wiki_link.ts @@ -13,7 +13,7 @@ import { resolvePath } from "$sb/lib/resolve.ts"; /** * Plugin to hide path prefix when the cursor is not inside. */ -export function cleanWikiLinkPlugin(editor: Client) { +export function cleanWikiLinkPlugin(client: Client) { return decoratorStateField((state) => { const widgets: any[] = []; // let parentRange: [number, number]; @@ -28,21 +28,20 @@ export function cleanWikiLinkPlugin(editor: Client) { if (!match) return; const [_fullMatch, page, pipePart, alias] = match; - const allPages = editor.space.listPages(); - let pageExists = !editor.fullSyncCompleted; + let pageExists = !client.fullSyncCompleted; let cleanPage = page; cleanPage = page.split(/[@$]/)[0]; - cleanPage = resolvePath(editor.currentPage!, cleanPage); + cleanPage = resolvePath(client.currentPage!, cleanPage); const lowerCasePageName = cleanPage.toLowerCase(); - for (const pageMeta of allPages) { - if (pageMeta.name.toLowerCase() === lowerCasePageName) { + for (const pageName of client.allKnownPages) { + if (pageName.toLowerCase() === lowerCasePageName) { pageExists = true; break; } } if ( cleanPage === "" || - editor.plugSpaceRemotePrimitives.isLikelyHandled(cleanPage) + client.plugSpaceRemotePrimitives.isLikelyHandled(cleanPage) ) { // Empty page name with local @anchor use or a link to a page that dynamically generated by a plug pageExists = true; @@ -90,19 +89,19 @@ export function cleanWikiLinkPlugin(editor: Client) { callback: (e) => { if (e.altKey) { // Move cursor into the link - return editor.editorView.dispatch({ + return client.editorView.dispatch({ selection: { anchor: from + 2 }, }); } // Dispatch click event to navigate there without moving the cursor const clickEvent: ClickEvent = { - page: editor.currentPage!, + page: client.currentPage!, ctrlKey: e.ctrlKey, metaKey: e.metaKey, altKey: e.altKey, pos: from, }; - editor.dispatchAppEvent("page:click", clickEvent).catch( + client.dispatchAppEvent("page:click", clickEvent).catch( console.error, ); }, diff --git a/web/editor_state.ts b/web/editor_state.ts index 409a53d..b1f2b4b 100644 --- a/web/editor_state.ts +++ b/web/editor_state.ts @@ -57,7 +57,7 @@ export function createEditorState( // Keyboard shortcuts from SETTINGS take precedense if (client.settings?.keyboardShortcuts) { for (const shortcut of client.settings.keyboardShortcuts) { - console.info("Configuring keyboard shortcut", shortcut); + // console.info("Configuring keyboard shortcut", shortcut); commandKeyBindings.push({ key: shortcut.key, mac: shortcut.mac, @@ -215,7 +215,7 @@ export function createEditorState( key: "Ctrl-k", mac: "Cmd-k", run: (): boolean => { - client.startPageNavigate().catch(console.error); + client.startPageNavigate(); return true; }, }, diff --git a/web/editor_ui.tsx b/web/editor_ui.tsx index fa70f15..7986880 100644 --- a/web/editor_ui.tsx +++ b/web/editor_ui.tsx @@ -44,7 +44,7 @@ export class MainUI { if (ev.touches.length === 2) { ev.stopPropagation(); ev.preventDefault(); - client.startPageNavigate().catch(console.error); + client.startPageNavigate(); } // Launch the command palette using a three-finger tap if (ev.touches.length === 3) { @@ -251,7 +251,7 @@ export class MainUI { icon: BookIcon, description: `Open page (${isMacLike() ? "Cmd-k" : "Ctrl-k"})`, callback: () => { - client.startPageNavigate().catch(console.error); + client.startPageNavigate(); }, }, { diff --git a/web/reducer.ts b/web/reducer.ts index abe082a..7ab6fd2 100644 --- a/web/reducer.ts +++ b/web/reducer.ts @@ -45,20 +45,26 @@ export default function reducer( ...state, syncFailures: action.syncSuccess ? 0 : state.syncFailures + 1, }; - case "start-navigate": { + case "update-page-list": { // Let's move over any "lastOpened" times to the "allPages" list const oldPageMeta = new Map( [...state.allPages].map((pm) => [pm.name, pm]), ); - for (const pageMeta of action.pages) { + for (const pageMeta of action.allPages) { const oldPageMetaItem = oldPageMeta.get(pageMeta.name); if (oldPageMetaItem && oldPageMetaItem.lastOpened) { pageMeta.lastOpened = oldPageMetaItem.lastOpened; } } + + return { + ...state, + allPages: action.allPages, + }; + } + case "start-navigate": { return { ...state, - allPages: action.pages, showPageNavigator: true, showCommandPalette: false, showFilterBox: false, diff --git a/web/space.ts b/web/space.ts index 876131f..b2e7515 100644 --- a/web/space.ts +++ b/web/space.ts @@ -14,9 +14,6 @@ export class Space { imageHeightCache = new LimitedMap(100); // url -> height widgetHeightCache = new LimitedMap(100); // bodytext -> height - // Note: this is "clean" PageMeta, it doesn't contain custom attributes (it's fetched from the store) - private cachedPageList: PageMeta[] = []; - debouncedImageCacheFlush = throttle(() => { this.ds.set(["cache", "imageHeight"], this.imageHeightCache).catch( console.error, @@ -72,25 +69,18 @@ export class Space { this.widgetHeightCache = new LimitedMap(100, widgetCache); } }); - // eventHook.addLocalListener("file:listed", (files: FileMeta[]) => { - // // console.log("Files listed", files); - // this.cachedPageList = files.filter(this.isListedPage).map( - // fileMetaToPageMeta, - // ); - // }); eventHook.addLocalListener("page:deleted", (pageName: string) => { if (this.watchedPages.has(pageName)) { // Stop watching deleted pages already this.watchedPages.delete(pageName); } }); - this.updatePageListCache().catch(console.error); + this.updatePageList().catch(console.error); } - public async updatePageListCache() { - console.log("Updating page list cache"); - // This will trigger appropriate events automatically - this.cachedPageList = await this.fetchPageList(); + public async updatePageList() { + // The only reason to do this is to trigger events + await this.fetchPageList(); } async deletePage(name: string): Promise { @@ -104,10 +94,6 @@ export class Space { ); } - listPages(): PageMeta[] { - return this.cachedPageList; - } - async listPlugs(): Promise { const files = await this.deduplicatedFileList(); return files @@ -139,10 +125,6 @@ export class Space { selfUpdate, ), ); - if (!this.cachedPageList.find((page) => page.name === pageMeta.name)) { - // New page, let's cache it - this.cachedPageList.push(pageMeta); - } // Note: we don't do very elaborate cache invalidation work here, quite quickly the cache will be flushed anyway return pageMeta; } finally { diff --git a/web/sync_service.ts b/web/sync_service.ts index 2372957..1749f30 100644 --- a/web/sync_service.ts +++ b/web/sync_service.ts @@ -382,7 +382,7 @@ export class NoSyncSyncService implements ISyncService { start() { setInterval(() => { // Trigger a page upload for change events - this.space.updatePageListCache().catch(console.error); + this.space.updatePageList().catch(console.error); }, spaceSyncInterval); } diff --git a/web/types.ts b/web/types.ts index ec94494..c002364 100644 --- a/web/types.ts +++ b/web/types.ts @@ -122,7 +122,8 @@ export type Action = | { type: "page-changed" } | { type: "page-saved" } | { type: "sync-change"; syncSuccess: boolean } - | { type: "start-navigate"; pages: PageMeta[] } + | { type: "update-page-list"; allPages: PageMeta[] } + | { type: "start-navigate" } | { type: "stop-navigate" } | { type: "update-commands";