WIP better matching

This commit is contained in:
Simon Cambier
2022-04-16 21:11:57 +02:00
parent 832e782ba8
commit 43b617e2af
7 changed files with 74 additions and 47 deletions

View File

@@ -24,8 +24,10 @@ function moveNoteSelection(ev: KeyboardEvent): void {
selectedNote.previous() selectedNote.previous()
break break
case "ArrowLeft": case "ArrowLeft":
ev.preventDefault()
break break
case "ArrowRight": case "ArrowRight":
ev.preventDefault()
break break
case "Enter": case "Enter":

View File

@@ -40,6 +40,7 @@ async function createOrOpenNote(item: ResultNote): Promise<void> {
} }
function onInputEnter(event: CustomEvent<ResultNote>): void { function onInputEnter(event: CustomEvent<ResultNote>): void {
console.log(event.detail)
openNote(event.detail) openNote(event.detail)
$modal.close() $modal.close()
} }

View File

@@ -2,17 +2,30 @@
import type { ResultNote } from "./globals" import type { ResultNote } from "./globals"
import { openNote } from "./notes" import { openNote } from "./notes"
import { modal, selectedNote } from "./stores" import { modal, selectedNote } from "./stores"
import { escapeHTML } from "./utils" import { escapeHTML, escapeRegex, getAllIndices, highlighter } from "./utils"
export let selected = false export let selected = false
export let note: ResultNote export let note: ResultNote
$: cleanContent = (() => { function cleanContent(content: string): string {
const content = escapeHTML(note.content) 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") const tmp = document.createElement("div")
tmp.innerHTML = content tmp.innerHTML = escapeHTML(content)
return tmp.textContent
})() return tmp.textContent ?? ''
}
$: reg = new RegExp(note.searchResult.terms.map(escapeRegex).join('|'), 'gi')
$: cleanedContent = cleanContent(note.content)
function onHover() { function onHover() {
$selectedNote = note $selectedNote = note
@@ -32,12 +45,12 @@ function onClick() {
on:click={onClick} on:click={onClick}
> >
<span class="omnisearch-result__title"> <span class="omnisearch-result__title">
{@html note.basename} {@html note.basename.replace(reg, highlighter)}
</span> </span>
<span class="omnisearch-result__counter"> <span class="omnisearch-result__counter">
{note.matches.length}&nbsp;{note.matches.length > 1 ? "matches" : "match"} {note.matches.length}&nbsp;{note.matches.length > 1 ? "matches" : "match"}
</span> </span>
<div class="omnisearch-result__body"> <div class="omnisearch-result__body">
{@html cleanContent} {@html cleanedContent.replace(reg, highlighter)}
</div> </div>
</div> </div>

View File

@@ -1,3 +1,5 @@
import type { SearchResult } from 'minisearch'
// Matches a wikiling that begins a string // Matches a wikiling that begins a string
export const regexWikilink = /^!?\[\[(?<name>.+?)(\|(?<alias>.+?))?\]\]/ export const regexWikilink = /^!?\[\[(?<name>.+?)(\|(?<alias>.+?))?\]\]/
export const regexLineSplit = /\r?\n|\r|((\.|\?|!)( |\r?\n|\r))/g 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 = { export type ResultNote = {
searchResult: SearchResult
path: string path: string
basename: string basename: string
content: string content: string

View File

@@ -8,10 +8,10 @@ export async function openNote(
newPane = false, newPane = false,
): Promise<void> { ): Promise<void> {
const app = get(plugin).app 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) // const fileCache = app.metadataCache.getFileCache(file)
// console.log(fileCache) // 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( const offset = content.indexOf(
item.matches[item.occurence].match.toLowerCase(), item.matches[item.occurence].match.toLowerCase(),
) )

View File

@@ -4,11 +4,9 @@ import type { IndexedNote, ResultNote } from './globals'
import { indexedNotes, plugin } from './stores' import { indexedNotes, plugin } from './stores'
import { get } from 'svelte/store' import { get } from 'svelte/store'
import { import {
escapeHTML,
escapeRegex, escapeRegex,
extractHeadingsFromCache, extractHeadingsFromCache,
getAllIndexes, getAllIndices,
highlighter,
wait, wait,
} from './utils' } from './utils'
@@ -67,40 +65,21 @@ export function getSuggestions(query: string): ResultNote[] {
if (!note) { if (!note) {
throw new Error(`Note "${result.id}" not indexed`) throw new Error(`Note "${result.id}" not indexed`)
} }
let basename = escapeHTML(note.basename) const reg = new RegExp(result.terms.map(escapeRegex).join('|'), 'gi')
let content = escapeHTML(note.content) const matches = getAllIndices(note.content, reg)
// 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 resultNote: ResultNote = { const resultNote: ResultNote = {
content, searchResult: result,
basename,
path: note.path,
matches,
occurence: 0, 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 return resultNote
}) })

View File

@@ -6,6 +6,7 @@ import {
regexYaml, regexYaml,
} from './globals' } from './globals'
import type { SearchMatch } from './globals' import type { SearchMatch } from './globals'
import { uniqBy } from 'lodash-es'
export function highlighter(str: string): string { export function highlighter(str: string): string {
return '<span class="search-result-file-matched-text">' + str + '</span>' 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` * Returns the positions of all occurences of `val` inside of `text`
* https://stackoverflow.com/a/58828841 * https://stackoverflow.com/a/58828841
* @param text * @param text
* @param val * @param regex
* @returns * @returns
*/ */
export function getAllIndexes(text: string, val: RegExp): SearchMatch[] { export function getAllIndices(text: string, regex: RegExp): SearchMatch[] {
return [...text.matchAll(val)] return [...text.matchAll(regex)]
.map(o => ({ match: o[0], index: o.index })) .map(o => ({ match: o[0], index: o.index }))
.filter(isSearchMatch) .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( export function extractHeadingsFromCache(
cache: CachedMetadata, cache: CachedMetadata,
level: number, level: number,