( root: string, query: string, limit = 80 )
| 798 | } |
| 799 | |
| 800 | export async function searchText( |
| 801 | root: string, |
| 802 | query: string, |
| 803 | limit = 80 |
| 804 | ): Promise<VaultTextSearchMatch[]> { |
| 805 | const trimmed = query.trim() |
| 806 | if (!trimmed) return [] |
| 807 | const needle = trimmed.toLowerCase() |
| 808 | const out: VaultTextSearchMatch[] = [] |
| 809 | const walk = async ( |
| 810 | folder: NoteFolder, |
| 811 | dirAbs: string, |
| 812 | topAbs: string, |
| 813 | isPrimaryRoot: boolean |
| 814 | ): Promise<void> => { |
| 815 | if (out.length >= limit) return |
| 816 | let entries |
| 817 | try { |
| 818 | entries = await fs.readdir(dirAbs, { withFileTypes: true }) |
| 819 | } catch { |
| 820 | return |
| 821 | } |
| 822 | for (const entry of entries) { |
| 823 | if (out.length >= limit) return |
| 824 | const full = path.join(dirAbs, entry.name) |
| 825 | if (entry.isDirectory()) { |
| 826 | if (entry.name.startsWith('.')) continue |
| 827 | if (isPrimaryRoot && dirAbs === topAbs && HIDDEN_PRIMARY_ROOT_NAMES.has(entry.name)) { |
| 828 | continue |
| 829 | } |
| 830 | await walk(folder, full, topAbs, isPrimaryRoot) |
| 831 | continue |
| 832 | } |
| 833 | if (!entry.isFile() || !entry.name.toLowerCase().endsWith('.md')) continue |
| 834 | let body = '' |
| 835 | try { |
| 836 | body = await fs.readFile(full, 'utf8') |
| 837 | } catch { |
| 838 | continue |
| 839 | } |
| 840 | const rel = toPosix(path.relative(root, full)) |
| 841 | const title = path.basename(full, path.extname(full)) |
| 842 | const lines = body.split('\n') |
| 843 | for (let i = 0; i < lines.length && out.length < limit; i++) { |
| 844 | if (lines[i].toLowerCase().includes(needle)) { |
| 845 | out.push({ |
| 846 | path: rel, |
| 847 | title, |
| 848 | folder, |
| 849 | lineNumber: i + 1, |
| 850 | lineText: lines[i].replace(/\s+/g, ' ').trim().slice(0, 220) |
| 851 | }) |
| 852 | } |
| 853 | } |
| 854 | } |
| 855 | } |
| 856 | for (const folder of LIVE_FOLDERS) { |
| 857 | if (out.length >= limit) break |
no test coverage detected