WIP better matching
This commit is contained in:
@@ -24,8 +24,10 @@ function moveNoteSelection(ev: KeyboardEvent): void {
|
||||
selectedNote.previous()
|
||||
break
|
||||
case "ArrowLeft":
|
||||
ev.preventDefault()
|
||||
break
|
||||
case "ArrowRight":
|
||||
ev.preventDefault()
|
||||
break
|
||||
|
||||
case "Enter":
|
||||
|
||||
@@ -40,6 +40,7 @@ async function createOrOpenNote(item: ResultNote): Promise<void> {
|
||||
}
|
||||
|
||||
function onInputEnter(event: CustomEvent<ResultNote>): void {
|
||||
console.log(event.detail)
|
||||
openNote(event.detail)
|
||||
$modal.close()
|
||||
}
|
||||
|
||||
@@ -2,17 +2,30 @@
|
||||
import type { ResultNote } from "./globals"
|
||||
import { openNote } from "./notes"
|
||||
import { modal, selectedNote } from "./stores"
|
||||
import { escapeHTML } from "./utils"
|
||||
import { escapeHTML, escapeRegex, getAllIndices, highlighter } from "./utils"
|
||||
|
||||
export let selected = false
|
||||
export let note: ResultNote
|
||||
|
||||
$: cleanContent = (() => {
|
||||
const content = escapeHTML(note.content)
|
||||
function cleanContent(content: string): string {
|
||||
const pos = content.toLowerCase().indexOf(note.searchResult.terms[0])
|
||||
const surroundLen = 180
|
||||
if (pos > -1) {
|
||||
const from = Math.max(0, pos - surroundLen)
|
||||
const to = Math.min(content.length - 1, pos + surroundLen)
|
||||
content =
|
||||
(from > 0 ? "…" : "") +
|
||||
content.slice(from, to).trim() +
|
||||
(to < content.length - 1 ? "…" : "")
|
||||
}
|
||||
const tmp = document.createElement("div")
|
||||
tmp.innerHTML = content
|
||||
return tmp.textContent
|
||||
})()
|
||||
tmp.innerHTML = escapeHTML(content)
|
||||
|
||||
return tmp.textContent ?? ''
|
||||
}
|
||||
$: reg = new RegExp(note.searchResult.terms.map(escapeRegex).join('|'), 'gi')
|
||||
|
||||
$: cleanedContent = cleanContent(note.content)
|
||||
|
||||
function onHover() {
|
||||
$selectedNote = note
|
||||
@@ -32,12 +45,12 @@ function onClick() {
|
||||
on:click={onClick}
|
||||
>
|
||||
<span class="omnisearch-result__title">
|
||||
{@html note.basename}
|
||||
{@html note.basename.replace(reg, highlighter)}
|
||||
</span>
|
||||
<span class="omnisearch-result__counter">
|
||||
{note.matches.length} {note.matches.length > 1 ? "matches" : "match"}
|
||||
</span>
|
||||
<div class="omnisearch-result__body">
|
||||
{@html cleanContent}
|
||||
{@html cleanedContent.replace(reg, highlighter)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { SearchResult } from 'minisearch'
|
||||
|
||||
// Matches a wikiling that begins a string
|
||||
export const regexWikilink = /^!?\[\[(?<name>.+?)(\|(?<alias>.+?))?\]\]/
|
||||
export const regexLineSplit = /\r?\n|\r|((\.|\?|!)( |\r?\n|\r))/g
|
||||
@@ -27,6 +29,7 @@ export const isSearchMatch = (o: { index?: number }): o is SearchMatch => {
|
||||
}
|
||||
|
||||
export type ResultNote = {
|
||||
searchResult: SearchResult
|
||||
path: string
|
||||
basename: string
|
||||
content: string
|
||||
|
||||
@@ -8,10 +8,10 @@ export async function openNote(
|
||||
newPane = false,
|
||||
): Promise<void> {
|
||||
const app = get(plugin).app
|
||||
const file = app.vault.getAbstractFileByPath(item.path) as TFile
|
||||
// const file = app.vault.getAbstractFileByPath(item.path) as TFile
|
||||
// const fileCache = app.metadataCache.getFileCache(file)
|
||||
// console.log(fileCache)
|
||||
const content = (await app.vault.cachedRead(file)).toLowerCase()
|
||||
const content = item.content// (await app.vault.cachedRead(file)).toLowerCase()
|
||||
const offset = content.indexOf(
|
||||
item.matches[item.occurence].match.toLowerCase(),
|
||||
)
|
||||
|
||||
@@ -4,11 +4,9 @@ import type { IndexedNote, ResultNote } from './globals'
|
||||
import { indexedNotes, plugin } from './stores'
|
||||
import { get } from 'svelte/store'
|
||||
import {
|
||||
escapeHTML,
|
||||
escapeRegex,
|
||||
extractHeadingsFromCache,
|
||||
getAllIndexes,
|
||||
highlighter,
|
||||
getAllIndices,
|
||||
wait,
|
||||
} from './utils'
|
||||
|
||||
@@ -67,40 +65,21 @@ export function getSuggestions(query: string): ResultNote[] {
|
||||
if (!note) {
|
||||
throw new Error(`Note "${result.id}" not indexed`)
|
||||
}
|
||||
let basename = escapeHTML(note.basename)
|
||||
let content = escapeHTML(note.content)
|
||||
|
||||
// Sort the terms from smaller to larger
|
||||
// and highlight them in the title and body
|
||||
const terms = result.terms.sort((a, b) => a.length - b.length)
|
||||
const reg = new RegExp(terms.map(escapeRegex).join('|'), 'gi')
|
||||
const matches = getAllIndexes(content, reg)
|
||||
|
||||
// If the body contains a searched term, find its position
|
||||
// and trim the text around it
|
||||
const pos = content.toLowerCase().indexOf(result.terms[0])
|
||||
const surroundLen = 180
|
||||
if (pos > -1) {
|
||||
const from = Math.max(0, pos - surroundLen)
|
||||
const to = Math.min(content.length - 1, pos + surroundLen)
|
||||
content =
|
||||
(from > 0 ? '…' : '') +
|
||||
content.slice(from, to).trim() +
|
||||
(to < content.length - 1 ? '…' : '')
|
||||
}
|
||||
|
||||
// console.log(matches)
|
||||
content = content.replace(reg, highlighter)
|
||||
basename = basename.replace(reg, highlighter)
|
||||
|
||||
const reg = new RegExp(result.terms.map(escapeRegex).join('|'), 'gi')
|
||||
const matches = getAllIndices(note.content, reg)
|
||||
const resultNote: ResultNote = {
|
||||
content,
|
||||
basename,
|
||||
path: note.path,
|
||||
matches,
|
||||
searchResult: result,
|
||||
occurence: 0,
|
||||
matches,
|
||||
...note,
|
||||
}
|
||||
if (note.basename === 'Search') {
|
||||
console.log('=======')
|
||||
// console.log([...note.content.matchAll(reg)])
|
||||
// console.log(reg)
|
||||
console.log(result)
|
||||
console.log(resultNote)
|
||||
}
|
||||
|
||||
return resultNote
|
||||
})
|
||||
|
||||
|
||||
35
src/utils.ts
35
src/utils.ts
@@ -6,6 +6,7 @@ import {
|
||||
regexYaml,
|
||||
} from './globals'
|
||||
import type { SearchMatch } from './globals'
|
||||
import { uniqBy } from 'lodash-es'
|
||||
|
||||
export function highlighter(str: string): string {
|
||||
return '<span class="search-result-file-matched-text">' + str + '</span>'
|
||||
@@ -76,15 +77,43 @@ export function escapeRegex(str: string): string {
|
||||
* Returns the positions of all occurences of `val` inside of `text`
|
||||
* https://stackoverflow.com/a/58828841
|
||||
* @param text
|
||||
* @param val
|
||||
* @param regex
|
||||
* @returns
|
||||
*/
|
||||
export function getAllIndexes(text: string, val: RegExp): SearchMatch[] {
|
||||
return [...text.matchAll(val)]
|
||||
export function getAllIndices(text: string, regex: RegExp): SearchMatch[] {
|
||||
return [...text.matchAll(regex)]
|
||||
.map(o => ({ match: o[0], index: o.index }))
|
||||
.filter(isSearchMatch)
|
||||
}
|
||||
|
||||
// export function getAllIndices(text: string, terms: string[]): SearchMatch[] {
|
||||
// let matches: SearchMatch[] = []
|
||||
// for (const term of terms) {
|
||||
// matches = [
|
||||
// ...matches,
|
||||
// ...[...text.matchAll(new RegExp(escapeRegex(term), 'gi'))]
|
||||
// .map(o => ({ match: o[0], index: o.index }))
|
||||
// .filter(isSearchMatch),
|
||||
// ]
|
||||
// }
|
||||
// return matches
|
||||
// // matches.sort((a, b) => b.match.length - a.match.length)
|
||||
// // return uniqBy(matches, 'index')
|
||||
// }
|
||||
|
||||
export function replaceAll(
|
||||
text: string,
|
||||
terms: string[],
|
||||
cb: (t: string) => string,
|
||||
): string {
|
||||
terms.sort((a, b) => a.length - b.length)
|
||||
const regs = terms.map(term => new RegExp(escapeRegex(term), 'gi'))
|
||||
for (const reg of regs) {
|
||||
text = text.replaceAll(reg, cb)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
export function extractHeadingsFromCache(
|
||||
cache: CachedMetadata,
|
||||
level: number,
|
||||
|
||||
Reference in New Issue
Block a user