Updated flow

This commit is contained in:
Simon Cambier
2022-04-19 22:41:34 +02:00
parent dc32bcad5b
commit d5f7382a98
7 changed files with 59 additions and 85 deletions

View File

@@ -1,7 +1,8 @@
<script lang="ts">
import { debounce } from "obsidian"
import { createEventDispatcher, onMount, tick } from "svelte"
import { searchQuery } from "./stores"
export let debouncedValue: string
let elInput: HTMLInputElement
let inputValue: string
@@ -10,11 +11,11 @@ const dispatch = createEventDispatcher()
onMount(async () => {
await tick()
elInput.focus()
elInput.value = $searchQuery
// elInput.value = $searchQuery
elInput.select()
})
const debouncedOnInput = debounce(() => ($searchQuery = inputValue), 100)
const debouncedOnInput = debounce(() => (debouncedValue = inputValue), 100)
function moveNoteSelection(ev: KeyboardEvent): void {
switch (ev.key) {
@@ -46,7 +47,7 @@ function moveNoteSelection(ev: KeyboardEvent): void {
}
}
</script>
{inputValue} - {debouncedValue}
<input
bind:this={elInput}
bind:value={inputValue}

View File

@@ -1,25 +1,29 @@
<script lang="ts">
import CmpInput from "./CmpInput.svelte"
import CmpResultInFile from "./CmpResultInFile.svelte"
import { excerptAfter, type SearchMatch } from "./globals"
import { modal, plugin, resultNotes } from "./stores"
import { excerptAfter, type ResultNote, type SearchMatch } from "./globals"
import { modal, plugin } from "./stores"
import { loopIndex } from "./utils"
import { tick } from "svelte"
import { MarkdownView } from "obsidian"
import CmpModalVault from "./CmpModalVault.svelte"
import { OmnisearchModal } from "./modal"
import { getSuggestions } from "./search"
export let canGoBack = false
let matches: SearchMatch[] = []
export let singleFilePath = ""
let groupedOffsets: number[] = []
let selectedIndex = 0
let searchQuery: string
let note: ResultNote | null = null
$: {
note = getSuggestions(searchQuery, { singleFilePath })[0] ?? null
selectedIndex = 0
scrollIntoView()
}
$: note = $resultNotes[0]
$: {
if (note) {
matches = note.matches
const groups = getGroups()
const groups = getGroups(note.matches)
groupedOffsets = groups.map((group) =>
Math.round((group.first()!.offset + group.last()!.offset) / 2)
)
@@ -27,20 +31,11 @@ $: {
// console.log(groupedOffsets)
}
}
$: {
if (canGoBack) {
$modal.onClose = () => {
if (canGoBack) {
new OmnisearchModal($plugin).open()
}
}
}
}
/**
* Group together close
*/
function getGroups(): SearchMatch[][] {
function getGroups(matches: SearchMatch[]): SearchMatch[][] {
const groups: SearchMatch[][] = []
let lastOffset = -1
while (true) {
@@ -99,6 +94,7 @@ function openSelection(): void {
<div class="modal-title">Omnisearch - File</div>
<CmpInput
bind:debouncedValue={searchQuery}
on:enter={openSelection}
on:arrow-up={() => moveIndex(-1)}
on:arrow-down={() => moveIndex(1)}

View File

@@ -1,26 +1,39 @@
<script lang="ts">
import { TFile } from "obsidian"
import { tick } from "svelte"
import { onMount, tick } from "svelte"
import CmpInput from "./CmpInput.svelte"
import CmpResultNote from "./CmpResultNote.svelte"
import type { ResultNote } from "./globals"
import { OmnisearchModal } from "./modal"
import { openNote } from "./notes"
import { modal, plugin, resultNotes, searchQuery } from "./stores"
import { getSuggestions } from "./search"
import { lastSearch, modal, plugin } from "./stores"
import { loopIndex } from "./utils"
let selectedIndex = 0
$: selectedNote = $resultNotes[selectedIndex]!
let searchQuery: string
let resultNotes: ResultNote[] = []
$: selectedNote = resultNotes[selectedIndex]!
searchQuery.subscribe((q) => {
$: {
if (searchQuery) {
resultNotes = getSuggestions(searchQuery)
$lastSearch = searchQuery
}
selectedIndex = 0
scrollIntoView()
}
onMount(async () => {
await tick()
searchQuery = $lastSearch
})
async function createOrOpenNote(item: ResultNote): Promise<void> {
try {
const file = await $plugin.app.vault.create(
$searchQuery + ".md",
"# " + $searchQuery
searchQuery + ".md",
"# " + searchQuery
)
await $plugin.app.workspace.openLinkText(file.path, "")
} catch (e) {
@@ -65,7 +78,7 @@ function onInputAltEnter(): void {
}
function moveIndex(dir: 1 | -1): void {
selectedIndex = loopIndex(selectedIndex + dir, $resultNotes.length)
selectedIndex = loopIndex(selectedIndex + dir, resultNotes.length)
scrollIntoView()
}
@@ -83,6 +96,7 @@ function scrollIntoView(): void {
<div class="modal-title">Omnisearch - Vault</div>
<CmpInput
bind:debouncedValue={searchQuery}
on:enter={onInputEnter}
on:shift-enter={onInputShiftEnter}
on:ctrl-enter={onInputCtrlEnter}
@@ -93,7 +107,7 @@ function scrollIntoView(): void {
<div class="modal-content">
<div class="prompt-results">
{#each $resultNotes as result, i}
{#each resultNotes as result, i}
<CmpResultNote
selected={i === selectedIndex}
note={result}

View File

@@ -32,11 +32,13 @@ export const isSearchMatch = (o: { offset?: number }): o is SearchMatch => {
}
export type ResultNote = {
// searchResult: SearchResult
score: number
path: string
basename: string
content: string
foundWords: string[]
matches: SearchMatch[]
occurence: number
}
export const SPACE_OR_PUNCTUATION =
/[|\n\r -#%-*,-/:;?@[-\]_{}\u00A0\u00A1\u00A7\u00AB\u00B6\u00B7\u00BB\u00BF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C77\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166E\u1680\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2000-\u200A\u2010-\u2029\u202F-\u2043\u2045-\u2051\u2053-\u205F\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4F\u3000-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]+/u

View File

@@ -2,7 +2,7 @@ import { Modal, TFile } from 'obsidian'
import type OmnisearchPlugin from './main'
import CmpModalVault from './CmpModalVault.svelte'
import CmpModalInFile from './CmpModalInFile.svelte'
import { inFileSearch, modal } from './stores'
import { modal } from './stores'
export class OmnisearchModal extends Modal {
constructor(plugin: OmnisearchPlugin, file?: TFile, canGoBack = false) {
@@ -15,7 +15,6 @@ export class OmnisearchModal extends Modal {
this.modalEl.append(closeEl)
this.modalEl.addClass('omnisearch-modal', 'prompt')
inFileSearch.set(file ?? null)
modal.set(this)
if (file) {
@@ -23,6 +22,7 @@ export class OmnisearchModal extends Modal {
target: this.modalEl,
props: {
canGoBack,
singleFilePath: file.path,
},
})
}

View File

@@ -1,13 +1,7 @@
import { Notice, TFile, type TAbstractFile } from 'obsidian'
import MiniSearch, { type SearchResult } from 'minisearch'
import type { IndexedNote, ResultNote, SearchMatch } from './globals'
import {
indexedNotes,
inFileSearch as singleFileSearch,
plugin,
resultNotes,
searchQuery,
} from './stores'
import { SPACE_OR_PUNCTUATION, type IndexedNote, type ResultNote, type SearchMatch } from './globals'
import { indexedNotes, plugin } from './stores'
import { get } from 'svelte/store'
import { extractHeadingsFromCache, stringsToRegex, wait } from './utils'
@@ -20,6 +14,7 @@ let minisearchInstance: MiniSearch<IndexedNote>
export async function initGlobalSearchIndex(): Promise<void> {
indexedNotes.set({})
minisearchInstance = new MiniSearch({
tokenize: text => text.split(SPACE_OR_PUNCTUATION),
idField: 'path',
fields: ['basename', 'content', 'headings1', 'headings2', 'headings3'],
})
@@ -48,7 +43,7 @@ export async function initGlobalSearchIndex(): Promise<void> {
}
// Listen to the query input to trigger a search
subscribeToQuery()
// subscribeToQuery()
}
/**
@@ -72,31 +67,6 @@ function search(query: string): SearchResult[] {
})
}
/**
* Automatically re-trigger the search when the query or the
* inFileSearch changes
*/
function subscribeToQuery(): void {
singleFileSearch.subscribe(async file => {
triggerQuery(get(searchQuery))
})
searchQuery.subscribe(triggerQuery)
async function triggerQuery(q: string): Promise<void> {
// If we're in "single file" mode, the search results array
// will contain a single result, related to this file
const results = get(singleFileSearch)
? getSuggestions(q, { singleFile: get(singleFileSearch) })
: getSuggestions(q)
// console.log('Search results')
// console.log(results)
// Save the results in the store
resultNotes.set(results)
}
}
/**
* Parses a text against a regex, and returns the { string, offset } matches
* @param text
@@ -123,7 +93,7 @@ export function getMatches(text: string, reg: RegExp): SearchMatch[] {
*/
export function getSuggestions(
query: string,
options?: Partial<{ singleFile: TFile | null }>,
options?: Partial<{ singleFilePath: string | null }>,
): ResultNote[] {
// Get the raw results
let results = search(query)
@@ -131,9 +101,8 @@ export function getSuggestions(
// Either keep the 50 first results,
// or the one corresponding to `singleFile`
if (options?.singleFile) {
const file = options.singleFile
const result = results.find(r => r.id === file.path)
if (options?.singleFilePath) {
const result = results.find(r => r.id === options.singleFilePath)
if (result) results = [result]
else results = []
}
@@ -150,8 +119,8 @@ export function getSuggestions(
const words = Object.keys(result.match)
const matches = getMatches(note.content, stringsToRegex(words))
const resultNote: ResultNote = {
score: result.score,
foundWords: words,
occurence: 0,
matches,
...note,
}

View File

@@ -30,17 +30,7 @@ function createIndexedNotes() {
/**
* If this field is set, the search will be limited to the given file
*/
export const inFileSearch = writable<TFile | null>(null)
/**
* The current search query
*/
export const searchQuery = writable<string>('')
/**
* The search results list, according to the current search query
*/
export const resultNotes = writable<ResultNote[]>([])
// export const inFileSearch = writable<TFile | null>(null)
/**
* A reference to the plugin instance
@@ -56,3 +46,5 @@ export const modal = writable<OmnisearchModal>()
* The entire list of indexed notes, constantly kept up-to-date.
*/
export const indexedNotes = createIndexedNotes()
export const lastSearch = writable('')