Removed in-file search
This commit is contained in:
		
							parent
							
								
									325933f538
								
							
						
					
					
						commit
						ba535966ab
					
				| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								src/main.ts
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								src/main.ts
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -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 () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user