+
+ {#if $inFileSearch}
+
+ {#if firstResult}
+ {#each firstResult.matches as match}
+
+ {/each}
+ {:else}
+ We found 0 result for your search here.
+ {/if}
+ {:else}
+
+ {#each $resultNotes as result}
+
+ {/each}
+ {/if}
-
- ↵to open
-
-
- ctrl ↵to open in a new pane
-
-
- shift ↵to create
-
-
-
escto dismiss
+
+
+ ↑↓to navigate
+
+
+ ↵to open
+
+
+ ctrl ↵
+ to open in a new pane
+
+
+ shift ↵
+ to create
+
+
+ escto dismiss
+
diff --git a/src/CmpNoteInternalResult.svelte b/src/CmpNoteInternalResult.svelte
new file mode 100644
index 0000000..182f846
--- /dev/null
+++ b/src/CmpNoteInternalResult.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+ {JSON.stringify(match)}
+
+
diff --git a/src/main.ts b/src/main.ts
index 0de27af..8b2856c 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,9 +1,9 @@
-import { Plugin, TFile } from 'obsidian'
+import { MarkdownView, Plugin, TFile } from 'obsidian'
import { OmnisearchModal } from './modal'
import { plugin } from './stores'
import {
addToIndex,
- instantiateMinisearch,
+ initGlobalSearchIndex,
removeFromIndex,
removeFromIndexByPath,
} from './search'
@@ -12,18 +12,34 @@ export default class OmnisearchPlugin extends Plugin {
async onload(): Promise
{
plugin.set(this)
- await instantiateMinisearch()
+ await initGlobalSearchIndex()
// Commands to display Omnisearch modal
this.addCommand({
id: 'show-modal',
- name: 'Open Omnisearch',
+ name: 'Vault search',
// hotkeys: [{ modifiers: ['Mod'], key: 'o' }],
callback: () => {
new OmnisearchModal(this).open()
},
})
+ this.addCommand({
+ id: 'show-modal-infile',
+ name: 'In-file search',
+ // hotkeys: [{ modifiers: ['Mod'], key: 'o' }],
+ checkCallback: (checking: boolean) => {
+ const view = this.app.workspace.getActiveViewOfType(MarkdownView)
+ if (view) {
+ if (!checking) {
+ new OmnisearchModal(this, view.file).open()
+ }
+ return true
+ }
+ return false
+ },
+ })
+
// Listeners to keep the search index up-to-date
this.registerEvent(
this.app.vault.on('create', file => {
diff --git a/src/modal.ts b/src/modal.ts
index faf1629..045daea 100644
--- a/src/modal.ts
+++ b/src/modal.ts
@@ -1,13 +1,19 @@
-import { Modal } from 'obsidian'
+import { Modal, TFile } from 'obsidian'
import type OmnisearchPlugin from './main'
import CmpModal from './CmpModal.svelte'
-import { modal } from './stores'
+import { inFileSearch, modal } from './stores'
export class OmnisearchModal extends Modal {
- constructor(plugin: OmnisearchPlugin) {
+ constructor(plugin: OmnisearchPlugin, file?: TFile) {
super(plugin.app)
+ // Remove all the default modal's children (except the close button)
+ // so that we can more easily customize it
+ const closeEl = this.containerEl.find('.modal-close-button')
+ this.modalEl.replaceChildren()
+ this.modalEl.append(closeEl)
this.modalEl.addClass('omnisearch-modal', 'prompt')
- this.modalEl.replaceChildren() // Remove all the default Modal's children
+
+ inFileSearch.set(file ?? null)
modal.set(this)
diff --git a/src/search.ts b/src/search.ts
index 89da1ad..224f047 100644
--- a/src/search.ts
+++ b/src/search.ts
@@ -1,21 +1,15 @@
import { Notice, TFile, type TAbstractFile } from 'obsidian'
-import MiniSearch from 'minisearch'
+import MiniSearch, { type SearchResult } from 'minisearch'
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'
+import { extractHeadingsFromCache, stringsToRegex, wait } from './utils'
-let minisearch: MiniSearch
+let minisearchInstance: MiniSearch
-export async function instantiateMinisearch(): Promise {
+export async function initGlobalSearchIndex(): Promise {
indexedNotes.set({})
- minisearch = new MiniSearch({
+ minisearchInstance = new MiniSearch({
idField: 'path',
fields: ['basename', 'content', 'headings1', 'headings2', 'headings3'],
})
@@ -54,23 +48,34 @@ export function getMatches(text: string, reg: RegExp): SearchMatch[] {
return matches
}
-export function getSuggestions(query: string): ResultNote[] {
- const results = minisearch
- .search(query, {
- prefix: true,
- fuzzy: term => (term.length > 4 ? 0.2 : false),
- combineWith: 'AND',
- boost: {
- basename: 2,
- headings1: 1.5,
- headings2: 1.3,
- headings3: 1.1,
- },
- })
- .sort((a, b) => b.score - a.score)
- .slice(0, 50)
- // console.log(`Omnisearch - Results for "${query}"`)
- // console.log(results)
+function search(query: string): SearchResult[] {
+ return minisearchInstance.search(query, {
+ prefix: true,
+ fuzzy: term => (term.length > 4 ? 0.2 : false),
+ combineWith: 'AND',
+ boost: {
+ basename: 2,
+ headings1: 1.5,
+ headings2: 1.3,
+ headings3: 1.1,
+ },
+ })
+}
+
+export function getSuggestions(
+ query: string,
+ options?: Partial<{ singleFile: TFile }>,
+): ResultNote[] {
+ let results = search(query)
+ if (options?.singleFile) {
+ const file = options.singleFile
+ const result = results.find(r => r.id === file.path)
+ if (result) results = [result]
+ else results = []
+ }
+ else {
+ results = results.sort((a, b) => b.score - a.score).slice(0, 50)
+ }
const suggestions = results.map(result => {
const note = indexedNotes.get(result.id)
@@ -80,17 +85,11 @@ export function getSuggestions(query: string): ResultNote[] {
const words = Object.keys(result.match)
const matches = getMatches(note.content, stringsToRegex(words))
const resultNote: ResultNote = {
- // searchResult: result,
foundWords: words,
occurence: 0,
matches,
...note,
}
- // if (note.basename === 'Search') {
- // console.log('=======')
- // console.log(result)
- // console.log(resultNote)
- // }
return resultNote
})
@@ -127,7 +126,7 @@ export async function addToIndex(file: TAbstractFile): Promise {
? extractHeadingsFromCache(fileCache, 3).join(' ')
: '',
}
- minisearch.add(note)
+ minisearchInstance.add(note)
indexedNotes.add(note)
}
catch (e) {
@@ -146,7 +145,7 @@ export function removeFromIndex(file: TAbstractFile): void {
export function removeFromIndexByPath(path: string): void {
const note = indexedNotes.get(path)
if (note) {
- minisearch.remove(note)
+ minisearchInstance.remove(note)
indexedNotes.remove(path)
}
}
diff --git a/src/stores.ts b/src/stores.ts
index 872a217..f5e1e18 100644
--- a/src/stores.ts
+++ b/src/stores.ts
@@ -1,3 +1,4 @@
+import type { TFile } from 'obsidian'
import { get, writable } from 'svelte/store'
import type { IndexedNote, ResultNote } from './globals'
import type OmnisearchPlugin from './main'
@@ -52,9 +53,37 @@ function createSelectedNote() {
}
}
+/**
+ * If this field is set, the search will be limited to the given file
+ */
+export const inFileSearch = writable(null)
+
+/**
+ * The current search query
+ */
export const searchQuery = writable('')
+
+/**
+ * The search results list, according to the current search query
+ */
export const resultNotes = writable([])
-export const plugin = writable()
-export const modal = writable()
+
+/**
+ * The currently selected/hovered note in the results list
+ */
export const selectedNote = createSelectedNote()
+
+/**
+ * A reference to the plugin instance
+ */
+export const plugin = writable()
+
+/**
+ * A reference to the modal instance
+ */
+export const modal = writable()
+
+/**
+ * The entire list of indexed notes, constantly kept up-to-date.
+ */
export const indexedNotes = createIndexedNotes()
diff --git a/src/utils.ts b/src/utils.ts
index 53bf8f6..5720bbc 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -5,7 +5,6 @@ import {
regexYaml,
} from './globals'
import type { SearchMatch } from './globals'
-import { uniqBy } from 'lodash-es'
export function highlighter(str: string): string {
return '' + str + ''