Merge branch 'develop'

This commit is contained in:
Simon Cambier
2023-11-11 10:08:17 +01:00
15 changed files with 194 additions and 89 deletions

View File

@@ -25,7 +25,6 @@
} }
.omnisearch-result__title > span { .omnisearch-result__title > span {
} }
.omnisearch-result__folder-path { .omnisearch-result__folder-path {
@@ -37,9 +36,9 @@
} }
.omnisearch-result__extension { .omnisearch-result__extension {
font-size: 0.7rem; font-size: 0.7rem;
color: var(--text-muted); color: var(--text-muted);
} }
.omnisearch-result__counter { .omnisearch-result__counter {
font-size: 0.7rem; font-size: 0.7rem;
@@ -62,7 +61,7 @@
.omnisearch-result__image-container { .omnisearch-result__image-container {
flex-basis: 20%; flex-basis: 20%;
text-align: right text-align: right;
} }
.omnisearch-highlight { .omnisearch-highlight {
@@ -78,6 +77,7 @@
.omnisearch-input-container { .omnisearch-input-container {
display: flex; display: flex;
align-items: center;
flex-direction: row; flex-direction: row;
gap: 5px; gap: 5px;
} }
@@ -86,6 +86,23 @@
.omnisearch-input-container { .omnisearch-input-container {
flex-direction: column; flex-direction: column;
} }
.omnisearch-input-container__buttons {
display: flex;
flex-direction: row;
width: 100%;
padding: 0 1em 0 1em;
gap: 1em;
}
.omnisearch-input-container__buttons > button {
flex-grow: 1;
}
}
@media only screen and (min-width: 600px) {
.omnisearch-input-container__buttons {
margin-right: 1em;
}
} }
.omnisearch-input-field { .omnisearch-input-field {

View File

@@ -1,6 +1,6 @@
{ {
"name": "scambier.obsidian-search", "name": "scambier.obsidian-search",
"version": "1.18.1", "version": "1.19.0-beta.1",
"description": "A search engine for Obsidian", "description": "A search engine for Obsidian",
"main": "dist/main.js", "main": "dist/main.js",
"scripts": { "scripts": {

View File

@@ -217,6 +217,9 @@ class CacheManager {
await database.searchHistory.bulkAdd(history) await database.searchHistory.bulkAdd(history)
} }
/**
* @returns The search history, in reverse chronological order
*/
public async getSearchHistory(): Promise<ReadonlyArray<string>> { public async getSearchHistory(): Promise<ReadonlyArray<string>> {
const data = (await database.searchHistory.toArray()) const data = (await database.searchHistory.toArray())
.reverse() .reverse()

View File

@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import InputSearch from './InputSearch.svelte' import InputSearch from './InputSearch.svelte'
import { import {
Action, Action,
eventBus, eventBus,
excerptAfter, excerptAfter,
type ResultNote, type ResultNote,
@@ -9,7 +9,7 @@
} from 'src/globals' } from 'src/globals'
import { getCtrlKeyLabel, loopIndex } from 'src/tools/utils' import { getCtrlKeyLabel, loopIndex } from 'src/tools/utils'
import { onDestroy, onMount, tick } from 'svelte' import { onDestroy, onMount, tick } from 'svelte'
import { MarkdownView } from 'obsidian' import { MarkdownView, App, Platform } from 'obsidian'
import ModalContainer from './ModalContainer.svelte' import ModalContainer from './ModalContainer.svelte'
import { import {
OmnisearchInFileModal, OmnisearchInFileModal,
@@ -24,6 +24,7 @@
export let parent: OmnisearchVaultModal | null = null export let parent: OmnisearchVaultModal | null = null
export let singleFilePath = '' export let singleFilePath = ''
export let previousQuery: string | undefined export let previousQuery: string | undefined
export let app: App
let searchQuery: string let searchQuery: string
let groupedOffsets: number[] = [] let groupedOffsets: number[] = []
@@ -150,7 +151,13 @@
<InputSearch <InputSearch
on:input="{e => (searchQuery = e.detail)}" on:input="{e => (searchQuery = e.detail)}"
placeholder="Omnisearch - File" placeholder="Omnisearch - File"
initialValue="{previousQuery}" /> initialValue="{previousQuery}">
<div class="omnisearch-input-container__buttons">
{#if Platform.isMobile}
<button on:click="{switchToVaultModal}">Vault search</button>
{/if}
</div>
</InputSearch>
<ModalContainer> <ModalContainer>
{#if groupedOffsets.length && note} {#if groupedOffsets.length && note}

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { MarkdownView, Notice, TFile } from 'obsidian' import { App, MarkdownView, Notice, Platform, TFile } from 'obsidian'
import { onDestroy, onMount, tick } from 'svelte' import { onDestroy, onMount, tick } from 'svelte'
import InputSearch from './InputSearch.svelte' import InputSearch from './InputSearch.svelte'
import ModalContainer from './ModalContainer.svelte' import ModalContainer from './ModalContainer.svelte'
@@ -29,9 +29,12 @@
import { cacheManager } from '../cache-manager' import { cacheManager } from '../cache-manager'
import { searchEngine } from 'src/search/omnisearch' import { searchEngine } from 'src/search/omnisearch'
import { cancelable, CancelablePromise } from 'cancelable-promise' import { cancelable, CancelablePromise } from 'cancelable-promise'
import { debounce } from 'lodash-es'
export let modal: OmnisearchVaultModal export let modal: OmnisearchVaultModal
export let previousQuery: string | undefined export let previousQuery: string | undefined
export let app: App
let selectedIndex = 0 let selectedIndex = 0
let historySearchIndex = 0 let historySearchIndex = 0
let searchQuery: string | undefined let searchQuery: string | undefined
@@ -59,10 +62,7 @@
createInCurrentPaneKey = 'shift ↵' createInCurrentPaneKey = 'shift ↵'
} }
$: if (searchQuery) { $: if (searchQuery) {
searching = true updateResultsDebounced()
updateResults().then(() => {
searching = false
})
} else { } else {
searching = false searching = false
resultNotes = [] resultNotes = []
@@ -79,11 +79,11 @@
indexingStepDesc = 'Indexing files...' indexingStepDesc = 'Indexing files...'
break break
case IndexingStepType.WritingCache: case IndexingStepType.WritingCache:
updateResults() updateResultsDebounced()
indexingStepDesc = 'Updating cache...' indexingStepDesc = 'Updating cache...'
break break
default: default:
updateResults() updateResultsDebounced()
indexingStepDesc = '' indexingStepDesc = ''
break break
} }
@@ -102,9 +102,7 @@
eventBus.on('vault', Action.PrevSearchHistory, prevSearchHistory) eventBus.on('vault', Action.PrevSearchHistory, prevSearchHistory)
eventBus.on('vault', Action.NextSearchHistory, nextSearchHistory) eventBus.on('vault', Action.NextSearchHistory, nextSearchHistory)
await NotesIndex.refreshIndex() await NotesIndex.refreshIndex()
if (settings.showPreviousQueryResults) { await updateResultsDebounced()
previousQuery = (await cacheManager.getSearchHistory())[0]
}
}) })
onDestroy(() => { onDestroy(() => {
@@ -132,6 +130,7 @@
let cancelableQuery: CancelablePromise<ResultNote[]> | null = null let cancelableQuery: CancelablePromise<ResultNote[]> | null = null
async function updateResults() { async function updateResults() {
searching = true
// If search is already in progress, cancel it and start a new one // If search is already in progress, cancel it and start a new one
if (cancelableQuery) { if (cancelableQuery) {
cancelableQuery.cancel() cancelableQuery.cancel()
@@ -146,8 +145,12 @@
resultNotes = await cancelableQuery resultNotes = await cancelableQuery
selectedIndex = 0 selectedIndex = 0
await scrollIntoView() await scrollIntoView()
searching = false
} }
// Debounce this function to avoid multiple calls caused by Svelte reactivity
const updateResultsDebounced = debounce(updateResults, 0)
function onClick(evt?: MouseEvent | KeyboardEvent) { function onClick(evt?: MouseEvent | KeyboardEvent) {
if (!selectedNote) return if (!selectedNote) return
if (evt?.ctrlKey) { if (evt?.ctrlKey) {
@@ -282,9 +285,14 @@
initialValue="{searchQuery}" initialValue="{searchQuery}"
on:input="{e => (searchQuery = e.detail)}" on:input="{e => (searchQuery = e.detail)}"
placeholder="Omnisearch - Vault"> placeholder="Omnisearch - Vault">
{#if settings.showCreateButton} <div class="omnisearch-input-container__buttons">
<button on:click="{onClickCreateNote}">Create note</button> {#if settings.showCreateButton}
{/if} <button on:click="{onClickCreateNote}">Create note</button>
{/if}
{#if Platform.isMobile}
<button on:click="{switchToInFileModal}">In-File search</button>
{/if}
</div>
</InputSearch> </InputSearch>
{#if indexingStepDesc} {#if indexingStepDesc}
@@ -296,6 +304,7 @@
<ModalContainer> <ModalContainer>
{#each resultNotes as result, i} {#each resultNotes as result, i}
<ResultItemVault <ResultItemVault
app="{app}"
selected="{i === selectedIndex}" selected="{i === selectedIndex}"
note="{result}" note="{result}"
on:mousemove="{_ => (selectedIndex = i)}" on:mousemove="{_ => (selectedIndex = i)}"

View File

@@ -10,12 +10,13 @@
removeDiacritics, removeDiacritics,
} from '../tools/utils' } from '../tools/utils'
import ResultItemContainer from './ResultItemContainer.svelte' import ResultItemContainer from './ResultItemContainer.svelte'
import { setIcon } from 'obsidian' import { TFile, setIcon, App } from 'obsidian'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { stringsToRegex, getMatches, makeExcerpt, highlightText } from 'src/tools/text-processing' import { stringsToRegex, getMatches, makeExcerpt, highlightText } from 'src/tools/text-processing'
export let selected = false export let selected = false
export let note: ResultNote export let note: ResultNote
export let app: App
let imagePath: string | null = null let imagePath: string | null = null
let title = '' let title = ''
@@ -26,10 +27,8 @@
$: { $: {
imagePath = null imagePath = null
if (isFileImage(note.path)) { if (isFileImage(note.path)) {
// @ts-ignore const file = app.vault.getAbstractFileByPath(note.path)
const file = app.vault.getFiles().find(f => f.path === note.path) if (file instanceof TFile) {
if (file) {
// @ts-ignore
imagePath = app.vault.getResourcePath(file) imagePath = app.vault.getResourcePath(file)
} }
} }

View File

@@ -4,6 +4,7 @@ import ModalVault from './ModalVault.svelte'
import ModalInFile from './ModalInFile.svelte' import ModalInFile from './ModalInFile.svelte'
import { Action, eventBus, EventNames, isInputComposition } from '../globals' import { Action, eventBus, EventNames, isInputComposition } from '../globals'
import { settings } from '../settings' import { settings } from '../settings'
import { cacheManager } from 'src/cache-manager'
abstract class OmnisearchModal extends Modal { abstract class OmnisearchModal extends Modal {
protected constructor(app: App) { protected constructor(app: App) {
@@ -142,25 +143,38 @@ abstract class OmnisearchModal extends Modal {
} }
export class OmnisearchVaultModal extends OmnisearchModal { export class OmnisearchVaultModal extends OmnisearchModal {
/**
* Instanciate the Omnisearch vault modal
* @param app
* @param query The query to pre-fill the search field with
*/
constructor(app: App, query?: string) { constructor(app: App, query?: string) {
super(app) super(app)
// Get selected text // Selected text in the editor
const selection = app.workspace.getActiveViewOfType(MarkdownView)?.editor.getSelection() const selectedText = app.workspace
.getActiveViewOfType(MarkdownView)
?.editor.getSelection()
const cmp = new ModalVault({ cacheManager.getSearchHistory().then(history => {
target: this.modalEl, // Previously searched query (if enabled in settings)
props: { const previous = settings.showPreviousQueryResults ? history[0] : null
modal: this,
previousQuery: selection ?? query, // Instantiate and display the Svelte component
}, const cmp = new ModalVault({
target: this.modalEl,
props: {
app,
modal: this,
previousQuery: query || selectedText || previous || '',
},
})
this.onClose = () => {
// Since the component is manually created,
// we also need to manually destroy it
cmp.$destroy()
}
}) })
this.onClose = () => {
// Since the component is manually created,
// we also need to manually destroy it
cmp.$destroy()
}
} }
} }
@@ -176,6 +190,7 @@ export class OmnisearchInFileModal extends OmnisearchModal {
const cmp = new ModalInFile({ const cmp = new ModalInFile({
target: this.modalEl, target: this.modalEl,
props: { props: {
app,
modal: this, modal: this,
singleFilePath: file.path, singleFilePath: file.path,
parent: parent, parent: parent,

View File

@@ -125,3 +125,4 @@ const separators =
.slice(1, -1) .slice(1, -1)
export const SPACE_OR_PUNCTUATION_UNIQUE = new RegExp(`${separators}`, 'u') export const SPACE_OR_PUNCTUATION_UNIQUE = new RegExp(`${separators}`, 'u')
export const SPACE_OR_PUNCTUATION = new RegExp(`${separators}+`, 'u') export const SPACE_OR_PUNCTUATION = new RegExp(`${separators}+`, 'u')
export const BRACKETS_AND_SPACE = /[|\[\]\(\)<>\{\} \t\n\r]/u

View File

@@ -1,4 +1,4 @@
import { Notice, Platform, Plugin } from 'obsidian' import { App, Notice, Platform, Plugin } from 'obsidian'
import { import {
OmnisearchInFileModal, OmnisearchInFileModal,
OmnisearchVaultModal, OmnisearchVaultModal,
@@ -46,7 +46,7 @@ export default class OmnisearchPlugin extends Plugin {
return return
} }
await cleanOldCacheFiles() await cleanOldCacheFiles(this.app)
await OmnisearchCache.clearOldDatabases() await OmnisearchCache.clearOldDatabases()
registerAPI(this) registerAPI(this)
@@ -66,7 +66,7 @@ export default class OmnisearchPlugin extends Plugin {
id: 'show-modal', id: 'show-modal',
name: 'Vault search', name: 'Vault search',
callback: () => { callback: () => {
new OmnisearchVaultModal(app).open() new OmnisearchVaultModal(this.app).open()
}, },
}) })
@@ -75,12 +75,12 @@ export default class OmnisearchPlugin extends Plugin {
name: 'In-file search', name: 'In-file search',
editorCallback: (_editor, view) => { editorCallback: (_editor, view) => {
if (view.file) { if (view.file) {
new OmnisearchInFileModal(app, view.file).open() new OmnisearchInFileModal(this.app, view.file).open()
} }
}, },
}) })
app.workspace.onLayoutReady(async () => { this.app.workspace.onLayoutReady(async () => {
// Listeners to keep the search index up-to-date // Listeners to keep the search index up-to-date
this.registerEvent( this.registerEvent(
this.app.vault.on('create', file => { this.app.vault.on('create', file => {
@@ -155,7 +155,7 @@ export default class OmnisearchPlugin extends Plugin {
addRibbonButton(): void { addRibbonButton(): void {
this.ribbonButton = this.addRibbonIcon('search', 'Omnisearch', _evt => { this.ribbonButton = this.addRibbonIcon('search', 'Omnisearch', _evt => {
new OmnisearchVaultModal(app).open() new OmnisearchVaultModal(this.app).open()
}) })
} }
@@ -168,7 +168,7 @@ export default class OmnisearchPlugin extends Plugin {
private async populateIndex(): Promise<void> { private async populateIndex(): Promise<void> {
console.time('Omnisearch - Indexing total time') console.time('Omnisearch - Indexing total time')
indexingStep.set(IndexingStepType.ReadingFiles) indexingStep.set(IndexingStepType.ReadingFiles)
const files = app.vault.getFiles().filter(f => isFileIndexable(f.path)) const files = this.app.vault.getFiles().filter(f => isFileIndexable(f.path))
console.log(`Omnisearch - ${files.length} files total`) console.log(`Omnisearch - ${files.length} files total`)
console.log( console.log(
`Omnisearch - Cache is ${isCacheEnabled() ? 'enabled' : 'disabled'}` `Omnisearch - Cache is ${isCacheEnabled() ? 'enabled' : 'disabled'}`
@@ -243,7 +243,7 @@ export default class OmnisearchPlugin extends Plugin {
* Read the files and feed them to Minisearch * Read the files and feed them to Minisearch
*/ */
async function cleanOldCacheFiles() { async function cleanOldCacheFiles(app: App) {
const toDelete = [ const toDelete = [
`${app.vault.configDir}/plugins/omnisearch/searchIndex.json`, `${app.vault.configDir}/plugins/omnisearch/searchIndex.json`,
`${app.vault.configDir}/plugins/omnisearch/notesCache.json`, `${app.vault.configDir}/plugins/omnisearch/notesCache.json`,
@@ -264,12 +264,12 @@ async function cleanOldCacheFiles() {
function registerAPI(plugin: OmnisearchPlugin): void { function registerAPI(plugin: OmnisearchPlugin): void {
// Url scheme for obsidian://omnisearch?query=foobar // Url scheme for obsidian://omnisearch?query=foobar
plugin.registerObsidianProtocolHandler('omnisearch', params => { plugin.registerObsidianProtocolHandler('omnisearch', params => {
new OmnisearchVaultModal(app, params.query).open() new OmnisearchVaultModal(plugin.app, params.query).open()
}) })
// Public api // Public api
// @ts-ignore // @ts-ignore
globalThis['omnisearch'] = api globalThis['omnisearch'] = api
// Deprecated // Deprecated
;(app as any).plugins.plugins.omnisearch.api = api ;(plugin.app as any).plugins.plugins.omnisearch.api = api
} }

View File

@@ -1,6 +1,11 @@
import MiniSearch, { type Options, type SearchResult } from 'minisearch' import MiniSearch, { type Options, type SearchResult } from 'minisearch'
import type { DocumentRef, IndexedDocument, ResultNote } from '../globals' import type { DocumentRef, IndexedDocument, ResultNote } from '../globals'
import { chsRegex, getChsSegmenter, SPACE_OR_PUNCTUATION } from '../globals' import {
BRACKETS_AND_SPACE,
chsRegex,
getChsSegmenter,
SPACE_OR_PUNCTUATION,
} from '../globals'
import { settings } from '../settings' import { settings } from '../settings'
import { import {
chunkArray, chunkArray,
@@ -17,6 +22,8 @@ import { sortBy } from 'lodash-es'
import { getMatches, stringsToRegex } from 'src/tools/text-processing' import { getMatches, stringsToRegex } from 'src/tools/text-processing'
const tokenize = (text: string): string[] => { const tokenize = (text: string): string[] => {
const words = text.split(BRACKETS_AND_SPACE)
let tokens = text.split(SPACE_OR_PUNCTUATION) let tokens = text.split(SPACE_OR_PUNCTUATION)
// Split hyphenated tokens // Split hyphenated tokens
@@ -25,15 +32,22 @@ const tokenize = (text: string): string[] => {
// Split camelCase tokens into "camel" and "case // Split camelCase tokens into "camel" and "case
tokens = [...tokens, ...tokens.flatMap(splitCamelCase)] tokens = [...tokens, ...tokens.flatMap(splitCamelCase)]
// Add whole words (aka "not tokens")
tokens = [...tokens, ...words]
// When enabled, we only use the chsSegmenter, // When enabled, we only use the chsSegmenter,
// and not the other custom tokenizers // and not the other custom tokenizers
const chsSegmenter = getChsSegmenter() const chsSegmenter = getChsSegmenter()
if (chsSegmenter) { if (chsSegmenter) {
tokens = tokens.flatMap(word => const chs = tokens.flatMap(word =>
chsRegex.test(word) ? chsSegmenter.cut(word) : [word] chsRegex.test(word) ? chsSegmenter.cut(word) : [word]
) )
tokens = [...tokens, ...chs]
} }
// Remove duplicates
tokens = [...new Set(tokens)]
return tokens return tokens
} }

View File

@@ -61,6 +61,7 @@ export interface OmnisearchSettings extends WeightingSettings {
fuzziness: '0' | '1' | '2' fuzziness: '0' | '1' | '2'
httpApiEnabled: boolean httpApiEnabled: boolean
httpApiPort: string httpApiPort: string
httpApiNotice: boolean
} }
/** /**
@@ -74,7 +75,7 @@ export class SettingsTab extends PluginSettingTab {
plugin: OmnisearchPlugin plugin: OmnisearchPlugin
constructor(plugin: OmnisearchPlugin) { constructor(plugin: OmnisearchPlugin) {
super(app, plugin) super(plugin.app, plugin)
this.plugin = plugin this.plugin = plugin
showExcerpt.subscribe(async v => { showExcerpt.subscribe(async v => {
@@ -87,7 +88,7 @@ export class SettingsTab extends PluginSettingTab {
const { containerEl } = this const { containerEl } = this
containerEl.empty() containerEl.empty()
if (app.loadLocalStorage(K_DISABLE_OMNISEARCH) == '1') { if (this.app.loadLocalStorage(K_DISABLE_OMNISEARCH) == '1') {
const span = containerEl.createEl('span') const span = containerEl.createEl('span')
span.innerHTML = `<strong style="color: var(--text-accent)">⚠️ OMNISEARCH IS DISABLED ⚠️</strong>` span.innerHTML = `<strong style="color: var(--text-accent)">⚠️ OMNISEARCH IS DISABLED ⚠️</strong>`
} }
@@ -220,6 +221,17 @@ export class SettingsTab extends PluginSettingTab {
}) })
) )
// Show previous query results
new Setting(containerEl)
.setName('Show previous query results')
.setDesc('Re-executes the previous query when opening Omnisearch.')
.addToggle(toggle =>
toggle.setValue(settings.showPreviousQueryResults).onChange(async v => {
settings.showPreviousQueryResults = v
await saveSettings(this.plugin)
})
)
// Respect excluded files // Respect excluded files
new Setting(containerEl) new Setting(containerEl)
.setName('Respect Obsidian\'s "Excluded Files"') .setName('Respect Obsidian\'s "Excluded Files"')
@@ -367,17 +379,6 @@ export class SettingsTab extends PluginSettingTab {
}) })
) )
// Show previous query results
new Setting(containerEl)
.setName('Show previous query results')
.setDesc('Re-executes the previous query when opening Omnisearch.')
.addToggle(toggle =>
toggle.setValue(settings.showPreviousQueryResults).onChange(async v => {
settings.showPreviousQueryResults = v
await saveSettings(this.plugin)
})
)
// Show "Create note" button // Show "Create note" button
const createBtnDesc = new DocumentFragment() const createBtnDesc = new DocumentFragment()
createBtnDesc.createSpan({}, span => { createBtnDesc.createSpan({}, span => {
@@ -504,6 +505,18 @@ export class SettingsTab extends PluginSettingTab {
await saveSettings(this.plugin) await saveSettings(this.plugin)
}) })
}) })
new Setting(containerEl)
.setName('Show a notification when the server starts')
.setDesc(
'Will display a notification if the server is enabled, at Obsidian startup.'
)
.addToggle(toggle =>
toggle.setValue(settings.httpApiNotice).onChange(async v => {
settings.httpApiNotice = v
await saveSettings(this.plugin)
})
)
} }
//#endregion HTTP Server //#endregion HTTP Server
@@ -543,9 +556,9 @@ export class SettingsTab extends PluginSettingTab {
.addToggle(toggle => .addToggle(toggle =>
toggle.setValue(isPluginDisabled()).onChange(async v => { toggle.setValue(isPluginDisabled()).onChange(async v => {
if (v) { if (v) {
app.saveLocalStorage(K_DISABLE_OMNISEARCH, '1') this.app.saveLocalStorage(K_DISABLE_OMNISEARCH, '1')
} else { } else {
app.saveLocalStorage(K_DISABLE_OMNISEARCH) // No value = unset this.app.saveLocalStorage(K_DISABLE_OMNISEARCH) // No value = unset
} }
new Notice('Omnisearch - Disabled. Please restart Obsidian.') new Notice('Omnisearch - Disabled. Please restart Obsidian.')
}) })
@@ -613,6 +626,7 @@ export const DEFAULT_SETTINGS: OmnisearchSettings = {
httpApiEnabled: false, httpApiEnabled: false,
httpApiPort: '51361', httpApiPort: '51361',
httpApiNotice: true,
welcomeMessage: '', welcomeMessage: '',
verboseLogging: false, verboseLogging: false,

View File

@@ -2,7 +2,7 @@ import * as http from 'http'
import * as url from 'url' import * as url from 'url'
import api from './api' import api from './api'
import { Notice } from 'obsidian' import { Notice } from 'obsidian'
import { saveSettings, settings } from 'src/settings' import { settings } from 'src/settings'
export function getServer() { export function getServer() {
const server = http.createServer(async function (req, res) { const server = http.createServer(async function (req, res) {
@@ -47,7 +47,9 @@ export function getServer() {
}, },
() => { () => {
console.log(`Omnisearch - Started HTTP server on port ${port}`) console.log(`Omnisearch - Started HTTP server on port ${port}`)
new Notice(`Omnisearch - Started HTTP server on port ${port}`) if (settings.httpApiNotice) {
new Notice(`Omnisearch - Started HTTP server on port ${port}`)
}
} }
) )
@@ -61,7 +63,9 @@ export function getServer() {
close() { close() {
server.close() server.close()
console.log(`Omnisearch - Terminated HTTP server`) console.log(`Omnisearch - Terminated HTTP server`)
new Notice(`Omnisearch - Terminated HTTP server`) if (settings.httpApiNotice) {
new Notice(`Omnisearch - Terminated HTTP server`)
}
}, },
} }
} }

View File

@@ -38,7 +38,7 @@ export async function openNote(
return return
} }
const pos = view.editor.offsetToPos(offset) const pos = view.editor.offsetToPos(offset)
pos.ch = 0 // pos.ch = 0
view.editor.setCursor(pos) view.editor.setCursor(pos)
view.editor.scrollIntoView({ view.editor.scrollIntoView({

View File

@@ -29,24 +29,46 @@ export function highlighterGroups(_substring: string, ...args: any[]) {
* @returns The html string with the matches highlighted * @returns The html string with the matches highlighted
*/ */
export function highlightText(text: string, matches: SearchMatch[]): string { export function highlightText(text: string, matches: SearchMatch[]): string {
if (!matches.length) {
return text
}
const chsSegmenter = getChsSegmenter()
try { try {
return text.replace( // Text to highlight
new RegExp( const src = new RegExp(
matches matches
.map(matchInfo => `\\b${escapeRegExp(matchInfo.match)}\\b`) .map(
.join('|'), // This regex will match the word (with \b word boundary)
'giu' // and, if ChsSegmenter is active, the simple string (without word boundary)
), matchItem =>
match => { `\\b${escapeRegExp(matchItem.match)}\\b${
const matchInfo = matches.find(info => chsSegmenter ? `|${escapeRegExp(matchItem.match)}` : ''
match.match(new RegExp(`\\b${escapeRegExp(info.match)}\\b`, 'giu')) }`
) )
if (matchInfo) { .join('|'),
return `<span class="${highlightClass}">${match}</span>` 'giu'
}
return match
}
) )
// Replacer function that will highlight the matches
const replacer = (match: string) => {
const matchInfo = matches.find(info =>
match.match(
new RegExp(
`\\b${escapeRegExp(info.match)}\\b${
chsSegmenter ? `|${escapeRegExp(info.match)}` : ''
}`,
'giu'
)
)
)
if (matchInfo) {
return `<span class="${highlightClass}">${match}</span>`
}
return match
}
// Effectively highlight the text
return text.replace(src, replacer)
} catch (e) { } catch (e) {
console.error('Omnisearch - Error in highlightText()', e) console.error('Omnisearch - Error in highlightText()', e)
return text return text
@@ -93,7 +115,7 @@ export function stringsToRegex(strings: string[]): RegExp {
')' + ')' +
`(${strings.map(s => escapeRegExp(s)).join('|')})` `(${strings.map(s => escapeRegExp(s)).join('|')})`
return new RegExp(`${joined}`, 'gu') return new RegExp(`${joined}`, 'gui')
} }
export function getMatches( export function getMatches(