Removed in-file search

This commit is contained in:
Simon Cambier 2025-06-21 14:03:33 +02:00
parent 325933f538
commit ba535966ab
4 changed files with 17 additions and 314 deletions

View File

@ -1,220 +0,0 @@
<script lang="ts">
import InputSearch from './InputSearch.svelte'
import {
Action,
eventBus,
excerptAfter,
type ResultNote,
type SearchMatch,
} from '../globals'
import { getCtrlKeyLabel, loopIndex } from '../tools/utils'
import { onDestroy, onMount, tick } from 'svelte'
import { MarkdownView, Platform } from 'obsidian'
import ModalContainer from './ModalContainer.svelte'
import { LocatorInFileModal, LocatorVaultModal } from '../components/modals'
import ResultItemInFile from './ResultItemInFile.svelte'
import { Query } from '../search/query'
import { openNote } from '../tools/notes'
import type LocatorPlugin from '../main'
export let plugin: LocatorPlugin
export let modal: LocatorInFileModal
export let parent: LocatorVaultModal | null = null
export let singleFilePath = ''
export let previousQuery: string | undefined
let searchQuery: string
let groupedOffsets: number[] = []
let selectedIndex = 0
let note: ResultNote | undefined
let query: Query
$: searchQuery = previousQuery ?? ''
onMount(() => {
eventBus.enable('infile')
eventBus.on('infile', Action.Enter, openSelection)
eventBus.on('infile', Action.OpenInNewPane, openSelectionInNewTab)
eventBus.on('infile', Action.ArrowUp, () => moveIndex(-1))
eventBus.on('infile', Action.ArrowDown, () => moveIndex(1))
eventBus.on('infile', Action.Tab, switchToVaultModal)
})
onDestroy(() => {
eventBus.disable('infile')
})
$: (async () => {
if (searchQuery) {
query = new Query(searchQuery, {
ignoreDiacritics: true,
ignoreArabicDiacritics: true,
})
note =
(
await plugin.searchEngine.getSuggestions(query, {
singleFilePath,
})
)[0] ?? null
}
selectedIndex = 0
await scrollIntoView()
})()
$: {
if (note) {
let groups = getGroups(note.matches)
// If there are quotes in the search,
// only show results that match at least one of the quotes
const exactTerms = query.getExactTerms()
if (exactTerms.length) {
groups = groups.filter(group =>
exactTerms.every(exact =>
group.some(match => match.match.includes(exact))
)
)
}
groupedOffsets = groups.map(group => Math.round(group.first()!.offset))
}
}
/**
* Group together close matches to reduce the number of results
*/
function getGroups(matches: SearchMatch[]): SearchMatch[][] {
const groups: SearchMatch[][] = []
let lastOffset = -1
let count = 0 // Avoid infinite loops
while (++count < 100) {
const group = getGroupedMatches(matches, lastOffset, excerptAfter)
if (!group.length) break
lastOffset = group.last()!.offset
groups.push(group)
}
return groups
}
function getGroupedMatches(
matches: SearchMatch[],
offsetFrom: number,
maxLen: number
): SearchMatch[] {
const first = matches.find(m => m.offset > offsetFrom)
if (!first) return []
return matches.filter(
m => m.offset > offsetFrom && m.offset <= first.offset + maxLen
)
}
function moveIndex(dir: 1 | -1): void {
selectedIndex = loopIndex(selectedIndex + dir, groupedOffsets.length)
scrollIntoView()
}
async function scrollIntoView(): Promise<void> {
await tick()
const elem = document.querySelector(`[data-result-id="${selectedIndex}"]`)
elem?.scrollIntoView({ behavior: 'auto', block: 'nearest' })
}
async function openSelectionInNewTab(): Promise<void> {
return openSelection(true)
}
async function openSelection(newTab = false): Promise<void> {
if (note) {
modal.close()
if (parent) parent.close()
// Open (or switch focus to) the note
const reg = plugin.textProcessor.stringsToRegex(note.foundWords)
reg.exec(note.content)
await openNote(plugin.app, note, reg.lastIndex, newTab)
// Move cursor to the match
const view = plugin.app.workspace.getActiveViewOfType(MarkdownView)
if (!view) {
// Not an editable document, so no cursor to place
return
// throw new Error('OmniSearch - No active MarkdownView')
}
const offset = groupedOffsets[selectedIndex] ?? 0
const pos = view.editor.offsetToPos(offset)
pos.ch = 0
view.editor.setCursor(pos)
view.editor.scrollIntoView({
from: { line: pos.line - 10, ch: 0 },
to: { line: pos.line + 10, ch: 0 },
})
}
}
function switchToVaultModal(): void {
new LocatorVaultModal(plugin, searchQuery ?? previousQuery).open()
modal.close()
}
</script>
<InputSearch
{plugin}
on:input={e => (searchQuery = e.detail)}
placeholder="Locator - File"
initialValue={previousQuery}>
<div class="omnisearch-input-container__buttons">
{#if Platform.isMobile}
<button on:click={switchToVaultModal}>Vault search</button>
{/if}
</div>
</InputSearch>
<ModalContainer>
{#if groupedOffsets.length && note}
{#each groupedOffsets as offset, i}
<ResultItemInFile
{plugin}
{offset}
{note}
index={i}
selected={i === selectedIndex}
on:mousemove={_e => (selectedIndex = i)}
on:click={evt => openSelection(evt.ctrlKey)}
on:auxclick={evt => {
if (evt.button == 1) openSelection(true)
}} />
{/each}
{:else}
<div style="text-align: center;">
We found 0 results for your search here.
</div>
{/if}
</ModalContainer>
<div class="prompt-instructions">
<div class="prompt-instruction">
<span class="prompt-instruction-command">↑↓</span><span>to navigate</span>
</div>
<div class="prompt-instruction">
<span class="prompt-instruction-command"></span><span>to open</span>
</div>
<div class="prompt-instruction">
<span class="prompt-instruction-command">tab</span>
<span>to switch to Vault Search</span>
</div>
<div class="prompt-instruction">
<span class="prompt-instruction-command">esc</span>
{#if !!parent}
<span>to go back to Vault Search</span>
{:else}
<span>to close</span>
{/if}
</div>
<div class="prompt-instruction">
<span class="prompt-instruction-command">{getCtrlKeyLabel()}</span>
<span>to open in a new pane</span>
</div>
</div>

View File

@ -1,34 +1,33 @@
<script lang="ts">
import { cancelable, CancelablePromise } from 'cancelable-promise'
import { debounce } from 'lodash-es'
import { MarkdownView, Notice, Platform, TFile } from 'obsidian'
import { onDestroy, onMount, tick } from 'svelte'
import InputSearch from './InputSearch.svelte'
import ModalContainer from './ModalContainer.svelte'
import {
type LocatorVaultModal,
} from '../components/modals'
import {
Action,
eventBus,
indexingStep,
IndexingStepType,
type ResultNote,
SPACE_OR_PUNCTUATION,
Action,
} from '../globals'
import type LocatorPlugin from '../main'
import { Query } from '../search/query'
import { createNote, openNote } from '../tools/notes'
import {
getCtrlKeyLabel,
getAltKeyLabel,
getCtrlKeyLabel,
getExtension,
isFilePDF,
loopIndex,
} from '../tools/utils'
import {
LocatorInFileModal,
type LocatorVaultModal,
} from '../components/modals'
import ResultItemVault from './ResultItemVault.svelte'
import { Query } from '../search/query'
import { cancelable, CancelablePromise } from 'cancelable-promise'
import { debounce } from 'lodash-es'
import type LocatorPlugin from '../main'
import InputSearch from './InputSearch.svelte'
import LazyLoader from './lazy-loader/LazyLoader.svelte'
import ModalContainer from './ModalContainer.svelte'
import ResultItemVault from './ResultItemVault.svelte'
let {
modal,
@ -107,7 +106,6 @@
eventBus.on('vault', Action.CreateNote, createNoteAndCloseModal)
eventBus.on('vault', Action.OpenInNewPane, openNoteInNewPane)
eventBus.on('vault', Action.InsertLink, insertLink)
eventBus.on('vault', Action.Tab, switchToInFileModal)
eventBus.on('vault', Action.ArrowUp, () => moveIndex(-1))
eventBus.on('vault', Action.ArrowDown, () => moveIndex(1))
eventBus.on('vault', Action.PrevSearchHistory, prevSearchHistory)
@ -271,34 +269,6 @@
modal.close()
}
function switchToInFileModal(): void {
// Do nothing if the selectedNote is a PDF,
// or if there is 0 match (e.g indexing in progress)
if (
selectedNote &&
(isFilePDF(selectedNote?.path) || !selectedNote?.matches.length)
) {
return
}
saveCurrentQuery()
modal.close()
if (selectedNote) {
// Open in-file modal for selected search result
const file = plugin.app.vault.getAbstractFileByPath(selectedNote.path)
if (file && file instanceof TFile) {
new LocatorInFileModal(plugin, file, searchQuery).open()
}
} else {
// Open in-file modal for active file
const view = plugin.app.workspace.getActiveViewOfType(MarkdownView)
if (view?.file) {
new LocatorInFileModal(plugin, view.file, searchQuery).open()
}
}
}
function moveIndex(dir: 1 | -1): void {
selectedIndex = loopIndex(selectedIndex + dir, resultNotes.length)
scrollIntoView()
@ -325,9 +295,6 @@
{#if plugin.settings.showCreateButton}
<button on:click={onClickCreateNote}>Create note</button>
{/if}
{#if Platform.isMobile}
<button on:click={switchToInFileModal}>In-File search</button>
{/if}
</div>
</InputSearch>

View File

@ -1,10 +1,9 @@
import { MarkdownView, Modal, TFile } from 'obsidian'
import type { Modifier } from 'obsidian'
import ModalVault from './ModalVault.svelte'
import ModalInFile from './ModalInFile.svelte'
import { MarkdownView, Modal } from 'obsidian'
import { mount, unmount } from 'svelte'
import { Action, eventBus, EventNames, isInputComposition } from '../globals'
import type LocatorPlugin from '../main'
import { mount, unmount } from 'svelte'
import ModalVault from './ModalVault.svelte'
abstract class LocatorModal extends Modal {
protected constructor(plugin: LocatorPlugin) {
@ -188,36 +187,3 @@ export class LocatorVaultModal extends LocatorModal {
})
}
}
export class LocatorInFileModal extends LocatorModal {
constructor(
plugin: LocatorPlugin,
file: TFile,
searchQuery: string = '',
parent?: LocatorModal
) {
super(plugin)
const cmp = mount(ModalInFile, {
target: this.modalEl,
props: {
plugin,
modal: this,
singleFilePath: file.path,
parent: parent,
previousQuery: searchQuery,
},
})
if (parent) {
// Hide the parent vault modal, and show it back when this one is closed
parent.containerEl.toggleVisibility(false)
}
this.onClose = () => {
if (parent) {
parent.containerEl.toggleVisibility(true)
}
unmount(cmp)
}
}
}

View File

@ -6,14 +6,14 @@ import {
type PluginManifest,
TFile,
} from 'obsidian'
import { LocatorInFileModal, LocatorVaultModal } from './components/modals'
import { LocatorVaultModal } from './components/modals'
import { Database } from './database'
import {
eventBus,
EventNames,
indexingStep,
IndexingStepType,
type TextExtractorApi
type TextExtractorApi,
} from './globals'
import { NotesIndexer } from './notes-indexer'
import { DocumentsRepository } from './repositories/documents-repository'
@ -84,16 +84,6 @@ export default class LocatorPlugin extends Plugin {
},
})
this.addCommand({
id: 'show-modal-infile',
name: 'In-file search',
editorCallback: (_editor, view) => {
if (view.file) {
new LocatorInFileModal(this, view.file).open()
}
},
})
const searchEngine = this.searchEngine
this.app.workspace.onLayoutReady(async () => {