import { useMemo } from "../preact/hooks.ts"; import { toTitleLc, revertTitleLc } from "../scrapbox-userscript-std/dom.ts"; import { Asearch } from "../deno-asearch/mod.ts"; import type { Scrapbox } from "../scrapbox-jp%2Ftypes/userscript.ts"; declare const scrapbox: Scrapbox; const getMaxDistance = [ 0, // 空文字のとき 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ]; export const useSearch = (text: string, limit: number): string[] => useMemo(() => { // 空白を`_`に置換して、空白一致できるようにする const textLc = toTitleLc(text.replace(/\s+/g, " ")); const forwardMatch = Asearch(`${textLc} `).match; const match = Asearch(` ${textLc} `).match; const maxDistance = getMaxDistance[textLc.length]; const textIgnoreSpace = revertTitleLc(textLc).trim(); // 空白をワイルドカードとして検索する // 検索文字列が空白を含むときのみ実行 const ignoreSpace = /\s/.test(text) ? { forwardMatch: Asearch(`${textIgnoreSpace} `).match, match: Asearch(` ${textIgnoreSpace} `).match, distance: getMaxDistance[textIgnoreSpace.length], } : undefined return scrapbox.Project.pages .flatMap((page) => { // 空白一致検索 { const result = forwardMatch(page.titleLc, maxDistance); if (result.found) return [{ point: result.distance, ...page }]; } { const result = match(page.titleLc, maxDistance); if (result.found) return [{ point: result.distance + 0.25, ...page }]; } if (!ignoreSpace) return []; // 空白をワイルドカードとして検索 { const result = ignoreSpace.forwardMatch(page.title, ignoreSpace.distance); if (result.found) return [{ point: result.distance + 0.5, ...page }]; } { const result = ignoreSpace.match(page.title, ignoreSpace.distance); if (result.found) return [{ point: result.distance + 0.75, ...page }]; } return []; }) .sort((a,b) => { // 1. 優先順位順 const diff = a.point - b.point; if (diff !== 0) return diff; // 2. 文字列が短い順 const ldiff = a.title.length - b.title.length if (ldiff !== 0 ) return ldiff; // 3. つながっているリンク順 if (a.exists !== b.exists) a.exists ? -1 : 1; // 4. 更新日時が新しい順 return b.updated - a.updated; }) .slice(0, limit) .map((page) => page.title) }, [text, limit, scrapbox.Project.pages]);