From 3027b6655f039d940207c4a52bf1ed424e1ea478 Mon Sep 17 00:00:00 2001 From: Simon Cambier Date: Sat, 9 Apr 2022 15:16:19 +0200 Subject: [PATCH] Search and highlight --- assets/styles.css | 18 +++++-- esbuild.config.mjs | 2 - src/main.ts | 129 +++++++++++++++++++++++++++++++-------------- src/styles.css | 4 -- 4 files changed, 105 insertions(+), 48 deletions(-) delete mode 100644 src/styles.css diff --git a/assets/styles.css b/assets/styles.css index 586fdea..6701820 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -1,4 +1,16 @@ -/* Sets all the text color to red! */ -body { - color: red; +.osresult__title { + /* font-size: var(--font-adaptive-normal); */ +} + +.osresult__body { + white-space: normal; + font-size: small; + word-wrap: normal; + + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + + color: var(--text-muted); } diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 851e793..dedef2a 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -48,8 +48,6 @@ esbuild outfile: path.join('./dist', 'main.js'), plugins: [ copy({ - // this is equal to process.cwd(), which means we use cwd path as base path to resolve `to` path - // if not specified, this plugin uses ESBuild.build outdir/outfile options as base path. assets: { from: ['./assets/*'], to: ['./'], diff --git a/src/main.ts b/src/main.ts index 79392aa..ca87a7d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,32 +3,43 @@ import MiniSearch from 'minisearch' import removeMarkdown from 'remove-markdown' type OmnisearchMatch = { + path: string + body: string + title: string +} + +type Note = { path: string content: string } export default class OmnisearchPlugin extends Plugin { - minisearch: MiniSearch + minisearch: MiniSearch files: TFile[] contents: Record - async onload(): Promise { - this.contents = {} - this.minisearch = new MiniSearch({ + setupIndex(): void { + this.minisearch = new MiniSearch({ idField: 'path', fields: ['content', 'title'], storeFields: ['path'], extractField: (document, fieldName) => { - if (fieldName === 'title') return getNoteTitle(document.content) + if (fieldName === 'title') return getFirstLine(document.content) return (document as any)[fieldName] as string }, }) + } + + async onload(): Promise { + this.contents = {} + + this.setupIndex() this.app.workspace.onLayoutReady(async () => { this.files = this.app.vault.getMarkdownFiles() for (const file of this.files) { const content = await this.app.vault.cachedRead(file) - this.contents[file.path] = truncateBody(getNoteBody(content)) + this.contents[file.path] = clearContent(content) // truncateText(clearContent(content)) this.minisearch.add({ content, path: file.path }) } console.log('minisearch loaded') @@ -52,64 +63,104 @@ class OmnisearchModal extends SuggestModal { constructor(plugin: OmnisearchPlugin) { super(plugin.app) this.plugin = plugin + this.setPlaceholder('Type to search through your notes') } getSuggestions(query: string): OmnisearchMatch[] { - const results = this.plugin.minisearch.search(query, { - prefix: true, - fuzzy: term => (term.length > 4 ? 0.2 : false), - combineWith: 'AND', - // processTerm: term => term.length <= minLength ? false : term, - boost: { title: 2 }, - }) + const results = this.plugin.minisearch + .search(query, { + prefix: true, + fuzzy: term => (term.length > 4 ? 0.2 : false), + combineWith: 'AND', + // processTerm: term => term.length <= minLength ? false : term, + boost: { title: 2 }, + }) + .sort((a, b) => b.score - a.score) + return results.map(result => { const file = this.plugin.files.find(f => f.path === result.id) + const content = this.plugin.contents[file.path] + + // Find position of result.terms[0] + const pos = content.toLowerCase().indexOf(result.terms[0].toLowerCase()) + + // Splice to get 150 chars before and after + let sliced = removeFirstLine( + content.slice( + Math.max(0, pos - 150), + Math.min(content.length - 1, pos + 150), + ), + ) + + // Highlight the word + const reg = new RegExp(result.terms[0], 'gi') + sliced = sliced.replace( + reg, + str => + '' + str + '', + ) + return { path: file.path, - content: this.plugin.contents[file.path], + title: getFirstLine(content), + body: sliced, } }) } - renderSuggestion(value: OmnisearchMatch, el: HTMLElement) { - el.createEl('div', { text: value.path }) - el.createEl('small', { text: value.content }) + renderSuggestion(value: OmnisearchMatch, el: HTMLElement): void { + el.createEl('div', { cls: 'osresult__title', text: value.title }) + const body = el.createEl('div', { cls: 'osresult__body' }) + body.innerHTML = value.body } - onChooseSuggestion(item: OmnisearchMatch, evt: MouseEvent | KeyboardEvent) { + onChooseSuggestion( + item: OmnisearchMatch, + evt: MouseEvent | KeyboardEvent, + ): void { throw new Error('Method not implemented.') } } -function truncateBody(body: string): string { - return body.substring(0, 200) + (body.length > 0 ? '...' : '') -} -function getNoteTitle(note: string): string { - return getFirstLine(removeMd(note)) -} -function getNoteBody(contents: string): string { - return truncateFirstLine(removeMd(contents)) +/** + * Strips the markdown and frontmatter + * @param text + */ +function clearContent(text: string): string { + return removeMarkdown(removeFrontMatter(text)) } +/** + * Returns the first line of the text + * @param text + * @returns + */ function getFirstLine(text: string): string { return splitLines(text.trim())[0] } -function splitLines(text: string): string[] { - return text.split(/\r?\n|\r/) -} -function removeMd(text: string): string { - return removeMarkdown(removeFrontMatter(text)) -} -function removeFrontMatter(text: string): string { - // Regex to recognize YAML Front Matter (at beginning of file, 3 hyphens, than any charecter, including newlines, then 3 hyphens). - const YAMLFrontMatter = /^---\s*\n(.*?)\n?^---\s?/ms - return text.replace(YAMLFrontMatter, '') -} - -function truncateFirstLine(text: string): string { +/** + * Removes the first line of the text + * @param text + * @returns + */ +function removeFirstLine(text: string): string { // https://stackoverflow.com/questions/2528076/delete-a-line-of-text-in-javascript const lines = splitLines(text.trim()) lines.splice(0, 1) return lines.join('\n') } + +function truncateText(text: string, len = 500): string { + return text.substring(0, len) + (text.length > 0 ? '...' : '') +} + +function splitLines(text: string): string[] { + return text.split(/\r?\n|\r/) +} + +function removeFrontMatter(text: string): string { + // Regex to recognize YAML Front Matter (at beginning of file, 3 hyphens, than any charecter, including newlines, then 3 hyphens). + const YAMLFrontMatter = /^---\s*\n(.*?)\n?^---\s?/ms + return text.replace(YAMLFrontMatter, '') +} diff --git a/src/styles.css b/src/styles.css deleted file mode 100644 index 586fdea..0000000 --- a/src/styles.css +++ /dev/null @@ -1,4 +0,0 @@ -/* Sets all the text color to red! */ -body { - color: red; -}