// TODO: Refactor this file // @ts-nocheck import { ContextSubmenuItem } from "../shims/typings"; import { getBasename, getLastNPathParts } from "../shims/utils"; import MiniSearch, { SearchResult } from "minisearch"; import { useEffect, useState } from "react"; import { useSelector } from "react-redux"; import { selectContextProviderDescriptions } from "../redux/selectors"; import { ideRequest } from "../shims/ide"; import { WebviewIde } from "../shims/webviewIde"; import { useWebviewListener } from "./useWebviewListener"; const MINISEARCH_OPTIONS = { prefix: true, fuzzy: 2, }; function useSubmenuContextProviders() { // TODO: Refresh periodically const [minisearches, setMinisearches] = useState<{ [id: string]: MiniSearch; }>({}); const [fallbackResults, setFallbackResults] = useState<{ [id: string]: ContextSubmenuItem[]; }>({}); const contextProviderDescriptions = useSelector( selectContextProviderDescriptions ); const [loaded, setLoaded] = useState(false); async function getOpenFileItems() { const openFiles = await new WebviewIde().getOpenFiles(); return openFiles.map((file) => { return { id: file, title: getBasename(file), description: getLastNPathParts(file, 2), providerTitle: "file", }; }); } useWebviewListener("refreshSubmenuItems", async (data) => { setLoaded(false); }); useWebviewListener("updateSubmenuItems", async (data) => { const minisearch = new MiniSearch({ fields: ["title", "description"], storeFields: ["id", "title", "description"], }); minisearch.addAll(data.submenuItems); setMinisearches((prev) => ({ ...prev, [data.provider]: minisearch })); if (data.provider === "file") { const openFiles = await getOpenFileItems(); setFallbackResults((prev) => ({ ...prev, file: openFiles })); } else { setFallbackResults((prev) => ({ ...prev, [data.provider]: data.submenuItems.slice(0, 6), })); } }); function addItem(providerTitle: string, item: ContextSubmenuItem) { if (!minisearches[providerTitle]) { return; } minisearches[providerTitle].add(item); } useEffect(() => { // Refresh open files periodically const interval = setInterval(async () => { const openFiles = await getOpenFileItems(); setFallbackResults((prev) => ({ ...prev, file: openFiles })); }, 2_000); return () => { clearInterval(interval); }; }, []); useEffect(() => { if (contextProviderDescriptions.length === 0 || loaded) { return; } setLoaded(true); contextProviderDescriptions.forEach(async (description) => { const minisearch = new MiniSearch({ fields: ["title", "description"], storeFields: ["id", "title", "description"], }); const items = await ideRequest("context/loadSubmenuItems", { title: description.title, }); minisearch.addAll(items); setMinisearches((prev) => ({ ...prev, [description.title]: minisearch })); if (description.title === "file") { const openFiles = await getOpenFileItems(); setFallbackResults((prev) => ({ ...prev, file: openFiles })); } else { setFallbackResults((prev) => ({ ...prev, [description.title]: items.slice(0, 6), })); } }); }, [contextProviderDescriptions, loaded]); function getSubmenuSearchResults( providerTitle: string | undefined, query: string ): SearchResult[] { if (providerTitle === undefined) { // Return search combined from all providers const results = Object.keys(minisearches).map((providerTitle) => { const results = minisearches[providerTitle].search( query, MINISEARCH_OPTIONS ); return results.map((result) => { return { ...result, providerTitle }; }); }); return results.flat().sort((a, b) => b.score - a.score); } if (!minisearches[providerTitle]) { return []; } return minisearches[providerTitle] .search(query, MINISEARCH_OPTIONS) .map((result) => { return { ...result, providerTitle }; }); } function getSubmenuContextItems( providerTitle: string | undefined, query: string, limit: number = 10 ): (ContextSubmenuItem & { providerTitle: string })[] { const results = getSubmenuSearchResults(providerTitle, query); if (results.length === 0) { return (fallbackResults[providerTitle] ?? []) .slice(0, limit) .map((result) => { return { ...result, providerTitle, }; }); } return results.slice(0, limit).map((result) => { return { id: result.id, title: result.title, description: result.description, providerTitle: result.providerTitle, }; }); } return { getSubmenuContextItems, addItem, }; } export default useSubmenuContextProviders;