Stricter TS, matches counter
This commit is contained in:
@@ -23,12 +23,12 @@ function moveNoteSelection(ev: KeyboardEvent): void {
|
||||
ev.preventDefault()
|
||||
selectedNote.previous()
|
||||
break
|
||||
case "ArrowLeft":
|
||||
ev.preventDefault()
|
||||
break
|
||||
case "ArrowRight":
|
||||
ev.preventDefault()
|
||||
break
|
||||
// case "ArrowLeft":
|
||||
// ev.preventDefault()
|
||||
// break
|
||||
// case "ArrowRight":
|
||||
// ev.preventDefault()
|
||||
// break
|
||||
|
||||
case "Enter":
|
||||
ev.preventDefault()
|
||||
|
||||
@@ -16,9 +16,10 @@ import {
|
||||
searchQuery.subscribe(async (q) => {
|
||||
const results = getSuggestions(q)
|
||||
resultNotes.set(results)
|
||||
if (results.length) {
|
||||
const firstResult = results[0]
|
||||
if (firstResult) {
|
||||
await tick()
|
||||
selectedNote.set(results[0])
|
||||
selectedNote.set(firstResult)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,16 +1,26 @@
|
||||
<script lang="ts">
|
||||
import type { ResultNote } from "./globals"
|
||||
import { openNote } from "./notes"
|
||||
import { getMatches } from "./search";
|
||||
import { modal, selectedNote } from "./stores"
|
||||
import { escapeHTML, escapeRegex, getAllIndices, highlighter } from "./utils"
|
||||
import { escapeHTML, highlighter, stringsToRegex } from "./utils"
|
||||
|
||||
export let selected = false
|
||||
export let note: ResultNote
|
||||
|
||||
// function getMatches(text: string) {
|
||||
// let match: RegExpExecArray | null = null
|
||||
// const matches: { term: string; index: number }[] = []
|
||||
// while (null !== (match = reg.exec(text))) {
|
||||
// matches.push({ term: match[0], index: match.index })
|
||||
// }
|
||||
// return matches
|
||||
// }
|
||||
|
||||
function cleanContent(content: string): string {
|
||||
const pos = content.toLowerCase().indexOf(note.searchResult.terms[0])
|
||||
const surroundLen = 180
|
||||
const pos = note.matches[0]?.offset ?? -1
|
||||
if (pos > -1) {
|
||||
const surroundLen = 180
|
||||
const from = Math.max(0, pos - surroundLen)
|
||||
const to = Math.min(content.length - 1, pos + surroundLen)
|
||||
content =
|
||||
@@ -18,13 +28,11 @@ function cleanContent(content: string): string {
|
||||
content.slice(from, to).trim() +
|
||||
(to < content.length - 1 ? "…" : "")
|
||||
}
|
||||
const tmp = document.createElement("div")
|
||||
tmp.innerHTML = escapeHTML(content)
|
||||
|
||||
return tmp.textContent ?? ''
|
||||
return escapeHTML(content)
|
||||
}
|
||||
$: reg = new RegExp(note.searchResult.terms.map(escapeRegex).join('|'), 'gi')
|
||||
|
||||
$: reg = stringsToRegex(note.foundWords)
|
||||
$: matches = getMatches(note.content, reg)
|
||||
$: cleanedContent = cleanContent(note.content)
|
||||
|
||||
function onHover() {
|
||||
@@ -45,10 +53,12 @@ function onClick() {
|
||||
on:click={onClick}
|
||||
>
|
||||
<span class="omnisearch-result__title">
|
||||
{@html note.basename.replace(reg, highlighter)}
|
||||
<!-- {@html note.basename.replace(reg, highlighter)} -->
|
||||
{@html note.basename}
|
||||
</span>
|
||||
|
||||
<span class="omnisearch-result__counter">
|
||||
{note.matches.length} {note.matches.length > 1 ? "matches" : "match"}
|
||||
{matches.length} {matches.length > 1 ? "matches" : "match"}
|
||||
</span>
|
||||
<div class="omnisearch-result__body">
|
||||
{@html cleanedContent.replace(reg, highlighter)}
|
||||
|
||||
@@ -22,17 +22,18 @@ export type IndexedNote = {
|
||||
|
||||
export type SearchMatch = {
|
||||
match: string
|
||||
index: number
|
||||
offset: number
|
||||
}
|
||||
export const isSearchMatch = (o: { index?: number }): o is SearchMatch => {
|
||||
return o.index !== undefined
|
||||
export const isSearchMatch = (o: { offset?: number }): o is SearchMatch => {
|
||||
return o.offset !== undefined
|
||||
}
|
||||
|
||||
export type ResultNote = {
|
||||
searchResult: SearchResult
|
||||
// searchResult: SearchResult
|
||||
path: string
|
||||
basename: string
|
||||
content: string
|
||||
foundWords: string[]
|
||||
matches: SearchMatch[]
|
||||
occurence: number
|
||||
}
|
||||
|
||||
13
src/notes.ts
13
src/notes.ts
@@ -1,20 +1,17 @@
|
||||
import { MarkdownView, TFile } from 'obsidian'
|
||||
import { MarkdownView } from 'obsidian'
|
||||
import { get } from 'svelte/store'
|
||||
import type { ResultNote } from './globals'
|
||||
import { plugin } from './stores'
|
||||
import { stringsToRegex } from './utils'
|
||||
|
||||
export async function openNote(
|
||||
item: ResultNote,
|
||||
newPane = false,
|
||||
): Promise<void> {
|
||||
const app = get(plugin).app
|
||||
// const file = app.vault.getAbstractFileByPath(item.path) as TFile
|
||||
// const fileCache = app.metadataCache.getFileCache(file)
|
||||
// console.log(fileCache)
|
||||
const content = item.content// (await app.vault.cachedRead(file)).toLowerCase()
|
||||
const offset = content.indexOf(
|
||||
item.matches[item.occurence].match.toLowerCase(),
|
||||
)
|
||||
const reg = stringsToRegex(item.foundWords)
|
||||
reg.exec(item.content)
|
||||
const offset = reg.lastIndex
|
||||
await app.workspace.openLinkText(item.path, '', newPane)
|
||||
|
||||
const view = app.workspace.getActiveViewOfType(MarkdownView)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { Notice, TFile, type TAbstractFile } from 'obsidian'
|
||||
import MiniSearch from 'minisearch'
|
||||
import type { IndexedNote, ResultNote } from './globals'
|
||||
import type { IndexedNote, ResultNote, SearchMatch } from './globals'
|
||||
import { indexedNotes, plugin } from './stores'
|
||||
import { get } from 'svelte/store'
|
||||
import {
|
||||
escapeRegex,
|
||||
extractHeadingsFromCache,
|
||||
getAllIndices,
|
||||
stringsToRegex,
|
||||
wait,
|
||||
} from './utils'
|
||||
|
||||
@@ -30,7 +31,8 @@ export async function instantiateMinisearch(): Promise<void> {
|
||||
}
|
||||
for (let i = 0; i < files.length; ++i) {
|
||||
if (i % 10 === 0) await wait(0)
|
||||
await addToIndex(files[i])
|
||||
const file = files[i]
|
||||
if (file) await addToIndex(file)
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
@@ -42,6 +44,16 @@ export async function instantiateMinisearch(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
export function getMatches(text: string, reg: RegExp): SearchMatch[] {
|
||||
let match: RegExpExecArray | null = null
|
||||
const matches: SearchMatch[] = []
|
||||
while ((match = reg.exec(text)) !== null) {
|
||||
const m = match[0]
|
||||
if (m) matches.push({ match: m, offset: match.index })
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
export function getSuggestions(query: string): ResultNote[] {
|
||||
const results = minisearch
|
||||
.search(query, {
|
||||
@@ -65,21 +77,20 @@ export function getSuggestions(query: string): ResultNote[] {
|
||||
if (!note) {
|
||||
throw new Error(`Note "${result.id}" not indexed`)
|
||||
}
|
||||
const reg = new RegExp(result.terms.map(escapeRegex).join('|'), 'gi')
|
||||
const matches = getAllIndices(note.content, reg)
|
||||
const words = Object.keys(result.match)
|
||||
const matches = getMatches(note.content, stringsToRegex(words))
|
||||
const resultNote: ResultNote = {
|
||||
searchResult: result,
|
||||
// searchResult: result,
|
||||
foundWords: words,
|
||||
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)
|
||||
}
|
||||
// if (note.basename === 'Search') {
|
||||
// console.log('=======')
|
||||
// console.log(result)
|
||||
// console.log(resultNote)
|
||||
// }
|
||||
return resultNote
|
||||
})
|
||||
|
||||
|
||||
38
src/utils.ts
38
src/utils.ts
@@ -21,38 +21,6 @@ export function escapeHTML(html: string): string {
|
||||
.replaceAll("'", ''')
|
||||
}
|
||||
|
||||
/**
|
||||
* The "title" line is the first line that isn't a wikilink
|
||||
* @param text
|
||||
* @returns
|
||||
*/
|
||||
export function getTitleLineIndex(lines: string[]): number {
|
||||
const index = lines.findIndex(l => !regexWikilink.test(l))
|
||||
return index > -1 ? index : 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "title" line from a text
|
||||
* @param text
|
||||
* @returns
|
||||
*/
|
||||
export function getTitleLine(text: string): string {
|
||||
const lines = splitLines(text.trim())
|
||||
return lines[getTitleLineIndex(lines)]
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the "title" line from a text
|
||||
* @param text
|
||||
* @returns
|
||||
*/
|
||||
export function removeTitleLine(text: string): string {
|
||||
const lines = splitLines(text.trim())
|
||||
const index = getTitleLineIndex(lines)
|
||||
lines.splice(index, 1)
|
||||
return lines.join('. ')
|
||||
}
|
||||
|
||||
export function splitLines(text: string): string[] {
|
||||
return text.split(regexLineSplit).filter(l => !!l && l.length > 2)
|
||||
}
|
||||
@@ -82,7 +50,7 @@ export function escapeRegex(str: string): string {
|
||||
*/
|
||||
export function getAllIndices(text: string, regex: RegExp): SearchMatch[] {
|
||||
return [...text.matchAll(regex)]
|
||||
.map(o => ({ match: o[0], index: o.index }))
|
||||
.map(o => ({ match: o[0], offset: o.index }))
|
||||
.filter(isSearchMatch)
|
||||
}
|
||||
|
||||
@@ -101,6 +69,10 @@ export function getAllIndices(text: string, regex: RegExp): SearchMatch[] {
|
||||
// // return uniqBy(matches, 'index')
|
||||
// }
|
||||
|
||||
export function stringsToRegex(strings: string[]): RegExp {
|
||||
return new RegExp(strings.map(escapeRegex).join('|'), 'gi')
|
||||
}
|
||||
|
||||
export function replaceAll(
|
||||
text: string,
|
||||
terms: string[],
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"node"
|
||||
],
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"baseUrl": ".",
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
|
||||
Reference in New Issue
Block a user