From dcfb77f5512bb5c285047ddccf63793a0d9ffd65 Mon Sep 17 00:00:00 2001 From: Simon Cambier Date: Tue, 21 May 2024 21:13:35 +0200 Subject: [PATCH] Massive refactoring to get rid of the deprecated global `app` instance --- src/cache-manager.ts | 16 ++-- src/components/ModalInFile.svelte | 4 +- src/components/ModalVault.svelte | 12 +-- src/components/modals.ts | 7 +- src/database.ts | 10 +-- src/globals.ts | 9 -- src/main.ts | 25 +++--- src/notes-index.ts | 3 +- src/search/omnisearch.ts | 49 ++++++----- src/search/query.ts | 4 +- src/search/tokenizer.ts | 4 +- src/settings.ts | 133 ++++++++++++++++-------------- src/stores/obsidian-app.ts | 4 +- src/tools/api-server.ts | 5 +- src/tools/api.ts | 7 +- src/tools/notes.ts | 6 +- src/tools/text-processing.ts | 10 ++- src/tools/utils.ts | 7 +- 18 files changed, 167 insertions(+), 148 deletions(-) diff --git a/src/cache-manager.ts b/src/cache-manager.ts index 1f14fec..a2e155d 100644 --- a/src/cache-manager.ts +++ b/src/cache-manager.ts @@ -4,7 +4,6 @@ import { getTextExtractor, type IndexedDocument, } from './globals' -import { database } from './database' import { extractHeadingsFromCache, getAliasesFromMetadata, @@ -24,10 +23,9 @@ import { import type { CanvasData } from 'obsidian/canvas' import type { AsPlainObject } from 'minisearch' import type MiniSearch from 'minisearch' -import { settings } from './settings' import { getObsidianApp } from './stores/obsidian-app' - -const app = getObsidianApp() +import { OmnisearchCache } from './database' +import { getSettings } from './settings' /** * This function is responsible for extracting the text from a file and @@ -37,6 +35,8 @@ const app = getObsidianApp() async function getAndMapIndexedDocument( path: string ): Promise { + const app = getObsidianApp() + const settings = getSettings() const file = app.vault.getAbstractFileByPath(path) if (!file) throw new Error(`Invalid file path: "${path}"`) if (!(file instanceof TFile)) throw new Error(`Not a TFile: "${path}"`) @@ -224,6 +224,7 @@ class CacheManager { return } this.nextQueryIsEmpty = false + const database = OmnisearchCache.getInstance() let history = await database.searchHistory.toArray() history = history.filter(s => s.query !== query).reverse() history.unshift({ query }) @@ -236,7 +237,7 @@ class CacheManager { * @returns The search history, in reverse chronological order */ public async getSearchHistory(): Promise> { - const data = (await database.searchHistory.toArray()) + const data = (await OmnisearchCache.getInstance().searchHistory.toArray()) .reverse() .map(o => o.query) if (this.nextQueryIsEmpty) { @@ -267,7 +268,9 @@ class CacheManager { data: AsPlainObject } | null> { try { - const cachedIndex = (await database.minisearch.toArray())[0] + const cachedIndex = ( + await OmnisearchCache.getInstance().minisearch.toArray() + )[0] return cachedIndex } catch (e) { new Notice( @@ -284,6 +287,7 @@ class CacheManager { indexed: Map ): Promise { const paths = Array.from(indexed).map(([k, v]) => ({ path: k, mtime: v })) + const database = OmnisearchCache.getInstance() await database.minisearch.clear() await database.minisearch.add({ date: new Date().toISOString(), diff --git a/src/components/ModalInFile.svelte b/src/components/ModalInFile.svelte index 81867f3..81c33da 100644 --- a/src/components/ModalInFile.svelte +++ b/src/components/ModalInFile.svelte @@ -18,8 +18,8 @@ import ResultItemInFile from './ResultItemInFile.svelte' import { Query } from 'src/search/query' import { openNote } from 'src/tools/notes' - import { searchEngine } from 'src/search/omnisearch' import { stringsToRegex } from 'src/tools/text-processing' + import { Omnisearch } from 'src/search/omnisearch' export let modal: OmnisearchInFileModal export let parent: OmnisearchVaultModal | null = null @@ -54,7 +54,7 @@ query = new Query(searchQuery) note = ( - await searchEngine.getSuggestions(query, { + await Omnisearch.getInstance().getSuggestions(query, { singleFilePath, }) )[0] ?? null diff --git a/src/components/ModalVault.svelte b/src/components/ModalVault.svelte index e31e14b..414b64f 100644 --- a/src/components/ModalVault.svelte +++ b/src/components/ModalVault.svelte @@ -24,12 +24,12 @@ } from 'src/components/modals' import ResultItemVault from './ResultItemVault.svelte' import { Query } from 'src/search/query' - import { settings } from '../settings' import * as NotesIndex from '../notes-index' import { cacheManager } from '../cache-manager' - import { searchEngine } from 'src/search/omnisearch' import { cancelable, CancelablePromise } from 'cancelable-promise' import { debounce } from 'lodash-es' + import { Omnisearch } from 'src/search/omnisearch' + import { getSettings } from 'src/settings' export let modal: OmnisearchVaultModal export let previousQuery: string | undefined @@ -51,7 +51,7 @@ $: selectedNote = resultNotes[selectedIndex] $: searchQuery = searchQuery ?? previousQuery - $: if (settings.openInNewPane) { + $: if (getSettings().openInNewPane) { openInNewPaneKey = '↵' openInCurrentPaneKey = getCtrlKeyLabel() + ' ↵' createInNewPaneKey = 'shift ↵' @@ -141,7 +141,7 @@ query = new Query(searchQuery) cancelableQuery = cancelable( new Promise(resolve => { - resolve(searchEngine.getSuggestions(query)) + resolve(Omnisearch.getInstance().getSuggestions(query)) }) ) resultNotes = await cancelableQuery @@ -299,7 +299,7 @@ on:input="{e => (searchQuery = e.detail)}" placeholder="Omnisearch - Vault">
- {#if settings.showCreateButton} + {#if getSettings().showCreateButton} {/if} {#if Platform.isMobile} @@ -329,7 +329,7 @@
{#if !resultNotes.length && searchQuery && !searching} We found 0 result for your search here. - {#if settings.simpleSearch && searchQuery + {#if getSettings().simpleSearch && searchQuery .split(SPACE_OR_PUNCTUATION) .some(w => w.length < 3)}
diff --git a/src/components/modals.ts b/src/components/modals.ts index e313869..66164f9 100644 --- a/src/components/modals.ts +++ b/src/components/modals.ts @@ -3,12 +3,13 @@ import type { Modifier } from 'obsidian' import ModalVault from './ModalVault.svelte' import ModalInFile from './ModalInFile.svelte' import { Action, eventBus, EventNames, isInputComposition } from '../globals' -import { settings } from '../settings' import { cacheManager } from 'src/cache-manager' +import { getSettings } from 'src/settings' abstract class OmnisearchModal extends Modal { protected constructor(app: App) { super(app) + const settings = getSettings() // Remove all the default modal's children // so that we can more easily customize it @@ -165,7 +166,9 @@ export class OmnisearchVaultModal extends OmnisearchModal { cacheManager.getSearchHistory().then(history => { // Previously searched query (if enabled in settings) - const previous = settings.showPreviousQueryResults ? history[0] : null + const previous = getSettings().showPreviousQueryResults + ? history[0] + : null // Instantiate and display the Svelte component const cmp = new ModalVault({ diff --git a/src/database.ts b/src/database.ts index 51c559b..649fc7c 100644 --- a/src/database.ts +++ b/src/database.ts @@ -6,7 +6,9 @@ import { getObsidianApp } from './stores/obsidian-app' export class OmnisearchCache extends Dexie { public static readonly dbVersion = 8 - public static readonly dbName = 'omnisearch/cache/' + getObsidianApp().appId + public static getDbName() { + return 'omnisearch/cache/' + getObsidianApp().appId + } private static instance: OmnisearchCache @@ -21,7 +23,7 @@ export class OmnisearchCache extends Dexie { > private constructor() { - super(OmnisearchCache.dbName) + super(OmnisearchCache.getDbName()) // Database structure this.version(OmnisearchCache.dbVersion).stores({ searchHistory: '++id', @@ -37,7 +39,7 @@ export class OmnisearchCache extends Dexie { public static async clearOldDatabases(): Promise { const toDelete = (await indexedDB.databases()).filter( db => - db.name === OmnisearchCache.dbName && + db.name === OmnisearchCache.getDbName() && // version multiplied by 10 https://github.com/dexie/Dexie.js/issues/59 db.version !== OmnisearchCache.dbVersion * 10 ) @@ -63,5 +65,3 @@ export class OmnisearchCache extends Dexie { await this.minisearch.clear() } } - -export const database = OmnisearchCache.getInstance() diff --git a/src/globals.ts b/src/globals.ts index 833fe4a..a63c64c 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -1,8 +1,6 @@ import { EventBus } from './tools/event-bus' import { writable } from 'svelte/store' -import { settings } from './settings' import type { TFile } from 'obsidian' -import { Platform } from 'obsidian' import { getObsidianApp } from './stores/obsidian-app' export const regexLineSplit = /\r?\n|\r|((\.|\?|!)( |\r?\n|\r))/g @@ -14,9 +12,6 @@ export const regexExtensions = /(?:^|\s)\.(\w+)/g export const excerptBefore = 100 export const excerptAfter = 300 -export const highlightClass = `suggestion-highlight omnisearch-highlight ${ - settings.highlight ? 'omnisearch-default-highlight' : '' -}` export const K_DISABLE_OMNISEARCH = 'omnisearch-disabled' export const eventBus = new EventBus() @@ -118,10 +113,6 @@ export function getTextExtractor(): TextExtractorApi | undefined { return (getObsidianApp() as any).plugins?.plugins?.['text-extractor']?.api } -export function isCacheEnabled(): boolean { - return !Platform.isIosApp && settings.useCache -} - export const SEPARATORS = /[|\t\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]/ .toString() diff --git a/src/main.ts b/src/main.ts index fe00b8b..fd2312b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,27 +4,22 @@ import { OmnisearchVaultModal, } from './components/modals' import { + getSettings, + isCacheEnabled, isPluginDisabled, loadSettings, saveSettings, - settings, SettingsTab, showExcerpt, } from './settings' -import { - eventBus, - EventNames, - indexingStep, - IndexingStepType, - isCacheEnabled, -} from './globals' +import { eventBus, EventNames, indexingStep, IndexingStepType } from './globals' import api, { notifyOnIndexed } from './tools/api' import { isFileIndexable, logDebug } from './tools/utils' -import { OmnisearchCache, database } from './database' +import { OmnisearchCache } from './database' import * as NotesIndex from './notes-index' -import { searchEngine } from './search/omnisearch' import { cacheManager } from './cache-manager' import { setObsidianApp } from './stores/obsidian-app' +import { Omnisearch } from './search/omnisearch' export default class OmnisearchPlugin extends Plugin { // FIXME: fix the type @@ -56,6 +51,7 @@ export default class OmnisearchPlugin extends Plugin { registerAPI(this) + const settings = getSettings() if (settings.ribbonIcon) { this.addRibbonButton() } @@ -85,6 +81,8 @@ export default class OmnisearchPlugin extends Plugin { }, }) + const searchEngine = Omnisearch.getInstance() + this.app.workspace.onLayoutReady(async () => { // Listeners to keep the search index up-to-date this.registerEvent( @@ -134,6 +132,7 @@ export default class OmnisearchPlugin extends Plugin { } async executeFirstLaunchTasks(): Promise { + const settings = getSettings() const code = '1.21.0' // if (settings.welcomeMessage !== code && getTextExtractor()) { // const welcome = new DocumentFragment() @@ -152,7 +151,7 @@ export default class OmnisearchPlugin extends Plugin { // Clear cache when disabling Omnisearch if (process.env.NODE_ENV === 'production') { - await database.clearCache() + await OmnisearchCache.getInstance().clearCache() } this.apiHttpServer.close() } @@ -180,6 +179,7 @@ export default class OmnisearchPlugin extends Plugin { // Map documents in the background // Promise.all(files.map(f => cacheManager.addToLiveCache(f.path))) + const searchEngine = Omnisearch.getInstance() if (isCacheEnabled()) { console.time('Omnisearch - Loading index from cache') indexingStep.set(IndexingStepType.LoadingCache) @@ -221,7 +221,8 @@ export default class OmnisearchPlugin extends Plugin { if ((diff.toRemove.length || diff.toAdd.length) && isCacheEnabled()) { indexingStep.set(IndexingStepType.WritingCache) - + const settings = getSettings() + // Disable settings.useCache while writing the cache, in case it freezes settings.useCache = false await saveSettings(this) diff --git a/src/notes-index.ts b/src/notes-index.ts index a6bffe5..09c99fd 100644 --- a/src/notes-index.ts +++ b/src/notes-index.ts @@ -1,5 +1,5 @@ import type { TAbstractFile } from 'obsidian' -import { searchEngine } from './search/omnisearch' +import { Omnisearch } from './search/omnisearch' // /** // * Index a non-existing note. @@ -42,6 +42,7 @@ export function markNoteForReindex(note: TAbstractFile): void { export async function refreshIndex(): Promise { const paths = [...notesToReindex].map(n => n.path) if (paths.length) { + const searchEngine = Omnisearch.getInstance() searchEngine.removeFromPaths(paths) await searchEngine.addFromPaths(paths) notesToReindex.clear() diff --git a/src/search/omnisearch.ts b/src/search/omnisearch.ts index 0f7f99b..2be2dd7 100644 --- a/src/search/omnisearch.ts +++ b/src/search/omnisearch.ts @@ -1,7 +1,6 @@ import MiniSearch, { type Options, type SearchResult } from 'minisearch' import type { DocumentRef, IndexedDocument, ResultNote } from '../globals' -import { settings } from '../settings' import { chunkArray, logDebug, removeDiacritics } from '../tools/utils' import { Notice } from 'obsidian' import type { Query } from './query' @@ -10,10 +9,21 @@ import { sortBy } from 'lodash-es' import { getMatches, stringsToRegex } from 'src/tools/text-processing' import { tokenizeForIndexing, tokenizeForSearch } from './tokenizer' import { getObsidianApp } from '../stores/obsidian-app' +import { getSettings } from 'src/settings' export class Omnisearch { + private static instance: Omnisearch + app = getObsidianApp() + settings = getSettings() + + public static getInstance(): Omnisearch { + if (!Omnisearch.instance) { + Omnisearch.instance = new Omnisearch(); + } + return Omnisearch.instance; + } public static readonly options: Options = { tokenize: tokenizeForIndexing, @@ -27,7 +37,7 @@ export class Omnisearch { return (doc as any)[fieldName] }, processTerm: (term: string) => - (settings.ignoreDiacritics ? removeDiacritics(term) : term).toLowerCase(), + (getSettings().ignoreDiacritics ? removeDiacritics(term) : term).toLowerCase(), idField: 'path', fields: [ 'basename', @@ -55,7 +65,7 @@ export class Omnisearch { // private previousResults: SearchResult[] = [] // private previousQuery: Query | null = null - constructor() { + private constructor() { this.minisearch = new MiniSearch(Omnisearch.options) } @@ -164,7 +174,7 @@ export class Omnisearch { logDebug('Starting search for', query) let fuzziness: number - switch (settings.fuzziness) { + switch (this.settings.fuzziness) { case '0': fuzziness = 0 break @@ -186,14 +196,14 @@ export class Omnisearch { fuzzy: term => term.length <= 3 ? 0 : term.length <= 5 ? fuzziness / 2 : fuzziness, boost: { - basename: settings.weightBasename, - directory: settings.weightDirectory, - aliases: settings.weightBasename, - headings1: settings.weightH1, - headings2: settings.weightH2, - headings3: settings.weightH3, - tags: settings.weightUnmarkedTags, - unmarkedTags: settings.weightUnmarkedTags, + basename: this.settings.weightBasename, + directory: this.settings.weightDirectory, + aliases: this.settings.weightBasename, + headings1: this.settings.weightH1, + headings2: this.settings.weightH2, + headings3: this.settings.weightH3, + tags: this.settings.weightUnmarkedTags, + unmarkedTags: this.settings.weightUnmarkedTags, }, // The query is already tokenized, don't tokenize again tokenize: text => [text], @@ -239,11 +249,11 @@ export class Omnisearch { logDebug( 'searching with downranked folders', - settings.downrankedFoldersFilters + this.settings.downrankedFoldersFilters ) // Hide or downrank files that are in Obsidian's excluded list - if (settings.hideExcluded) { + if (this.settings.hideExcluded) { // Filter the files out results = results.filter( result => @@ -269,10 +279,10 @@ export class Omnisearch { for (const result of results) { const path = result.id - if (settings.downrankedFoldersFilters.length > 0) { + if (this.settings.downrankedFoldersFilters.length > 0) { // downrank files that are in folders listed in the downrankedFoldersFilters let downrankingFolder = false - settings.downrankedFoldersFilters.forEach(filter => { + this.settings.downrankedFoldersFilters.forEach(filter => { if (path.startsWith(filter)) { // we don't want the filter to match the folder sources, e.g. // it needs to match a whole folder name @@ -289,7 +299,7 @@ export class Omnisearch { const pathPartsLength = pathParts.length for (let i = 0; i < pathPartsLength; i++) { const pathPart = pathParts[i] - if (settings.downrankedFoldersFilters.includes(pathPart)) { + if (this.settings.downrankedFoldersFilters.includes(pathPart)) { result.score /= 10 break } @@ -299,7 +309,7 @@ export class Omnisearch { // Boost custom properties const metadata = this.app.metadataCache.getCache(path) if (metadata) { - for (const { name, weight } of settings.weightCustomProperties) { + for (const { name, weight } of this.settings.weightCustomProperties) { const values = metadata?.frontmatter?.[name] if (values && result.terms.some(t => values.includes(t))) { logDebug(`Boosting field "${name}" x${weight} for ${path}`) @@ -379,7 +389,7 @@ export class Omnisearch { ): Promise { // Get the raw results let results: SearchResult[] - if (settings.simpleSearch) { + if (this.settings.simpleSearch) { results = await this.search(query, { prefixLength: 3, singleFilePath: options?.singleFilePath, @@ -450,4 +460,3 @@ export class Omnisearch { } } -export const searchEngine = new Omnisearch() diff --git a/src/search/query.ts b/src/search/query.ts index a6d7dba..7a7b283 100644 --- a/src/search/query.ts +++ b/src/search/query.ts @@ -1,4 +1,4 @@ -import { settings } from '../settings' +import { getSettings } from 'src/settings' import { removeDiacritics } from '../tools/utils' import { parse } from 'search-query-parser' @@ -15,7 +15,7 @@ export class Query { #inQuotes: string[] constructor(text = '') { - if (settings.ignoreDiacritics) { + if (getSettings().ignoreDiacritics) { text = removeDiacritics(text) } const parsed = parse(text.toLowerCase(), { diff --git a/src/search/tokenizer.ts b/src/search/tokenizer.ts index bd5fb50..33eb7d4 100644 --- a/src/search/tokenizer.ts +++ b/src/search/tokenizer.ts @@ -5,7 +5,7 @@ import { chsRegex, getChsSegmenter, } from 'src/globals' -import { settings } from 'src/settings' +import { getSettings } from 'src/settings' import { logDebug, splitCamelCase, splitHyphens } from 'src/tools/utils' const markdownLinkExtractor = require('markdown-link-extractor') @@ -38,7 +38,7 @@ function tokenizeChsWord(tokens: string[]): string[] { export function tokenizeForIndexing(text: string): string[] { const words = tokenizeWords(text) let urls: string[] = [] - if (settings.tokenizeUrls) { + if (getSettings().tokenizeUrls) { try { urls = markdownLinkExtractor(text) } catch (e) { diff --git a/src/settings.ts b/src/settings.ts index d9a4b41..20b41dd 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -8,14 +8,10 @@ import { SliderComponent, } from 'obsidian' import { writable } from 'svelte/store' -import { database } from './database' -import { - K_DISABLE_OMNISEARCH, - getTextExtractor, - isCacheEnabled, -} from './globals' +import { K_DISABLE_OMNISEARCH, getTextExtractor } from './globals' import type OmnisearchPlugin from './main' import { getObsidianApp } from './stores/obsidian-app' +import { OmnisearchCache } from './database' interface WeightingSettings { weightBasename: number @@ -95,6 +91,7 @@ export class SettingsTab extends PluginSettingTab { display(): void { const { containerEl } = this + const database = OmnisearchCache.getInstance() containerEl.empty() if (this.app.loadLocalStorage(K_DISABLE_OMNISEARCH) == '1') { @@ -479,38 +476,37 @@ export class SettingsTab extends PluginSettingTab { new Setting(containerEl) .setName( - `File name & declared aliases (default: ${DEFAULT_SETTINGS.weightBasename})` + `File name & declared aliases (default: ${getDefaultSettings().weightBasename})` ) .addSlider(cb => this.weightSlider(cb, 'weightBasename')) new Setting(containerEl) - .setName(`File directory (default: ${DEFAULT_SETTINGS.weightDirectory})`) + .setName(`File directory (default: ${getDefaultSettings().weightDirectory})`) .addSlider(cb => this.weightSlider(cb, 'weightDirectory')) new Setting(containerEl) - .setName(`Headings level 1 (default: ${DEFAULT_SETTINGS.weightH1})`) + .setName(`Headings level 1 (default: ${getDefaultSettings().weightH1})`) .addSlider(cb => this.weightSlider(cb, 'weightH1')) new Setting(containerEl) - .setName(`Headings level 2 (default: ${DEFAULT_SETTINGS.weightH2})`) + .setName(`Headings level 2 (default: ${getDefaultSettings().weightH2})`) .addSlider(cb => this.weightSlider(cb, 'weightH2')) new Setting(containerEl) - .setName(`Headings level 3 (default: ${DEFAULT_SETTINGS.weightH3})`) + .setName(`Headings level 3 (default: ${getDefaultSettings().weightH3})`) .addSlider(cb => this.weightSlider(cb, 'weightH3')) new Setting(containerEl) - .setName( - `Tags (default: ${DEFAULT_SETTINGS.weightUnmarkedTags})` - ) + .setName(`Tags (default: ${getDefaultSettings().weightUnmarkedTags})`) .addSlider(cb => this.weightSlider(cb, 'weightUnmarkedTags')) //#region Specific tags new Setting(containerEl) .setName('Header properties fields') - .setDesc('You can set custom weights for values of header properties (e.g. "keywords").') - + .setDesc( + 'You can set custom weights for values of header properties (e.g. "keywords").' + ) for (let i = 0; i < settings.weightCustomProperties.length; i++) { const item = settings.weightCustomProperties[i] @@ -547,14 +543,13 @@ export class SettingsTab extends PluginSettingTab { } // Add a new custom tag - new Setting(containerEl) - .addButton(btn => { - btn.setButtonText('Add a new property') - btn.onClick(cb => { - settings.weightCustomProperties.push({ name: '', weight: 1 }) - this.display() - }) + new Setting(containerEl).addButton(btn => { + btn.setButtonText('Add a new property') + btn.onClick(cb => { + settings.weightCustomProperties.push({ name: '', weight: 1 }) + this.display() }) + }) //#endregion Specific tags @@ -712,52 +707,60 @@ export class SettingsTab extends PluginSettingTab { } } -const app = getObsidianApp() +function getDefaultSettings(): OmnisearchSettings { + const app = getObsidianApp() + return { + useCache: true, + hideExcluded: false, + downrankedFoldersFilters: [] as string[], + ignoreDiacritics: true, + indexedFileTypes: [] as string[], + PDFIndexing: false, + officeIndexing: false, + imagesIndexing: false, + unsupportedFilesIndexing: 'default', + splitCamelCase: false, + openInNewPane: false, + vimLikeNavigationShortcut: app.vault.getConfig('vimMode') as boolean, -export const DEFAULT_SETTINGS: OmnisearchSettings = { - useCache: true, - hideExcluded: false, - downrankedFoldersFilters: [] as string[], - ignoreDiacritics: true, - indexedFileTypes: [] as string[], - PDFIndexing: false, - officeIndexing: false, - imagesIndexing: false, - unsupportedFilesIndexing: 'default', - splitCamelCase: false, - openInNewPane: false, - vimLikeNavigationShortcut: app.vault.getConfig('vimMode') as boolean, + ribbonIcon: true, + showExcerpt: true, + renderLineReturnInExcerpts: true, + showCreateButton: false, + highlight: true, + showPreviousQueryResults: true, + simpleSearch: false, + tokenizeUrls: false, + fuzziness: '1', - ribbonIcon: true, - showExcerpt: true, - renderLineReturnInExcerpts: true, - showCreateButton: false, - highlight: true, - showPreviousQueryResults: true, - simpleSearch: false, - tokenizeUrls: false, - fuzziness: '1', + weightBasename: 3, + weightDirectory: 2, + weightH1: 1.5, + weightH2: 1.3, + weightH3: 1.1, + weightUnmarkedTags: 1.1, + weightCustomProperties: [] as { name: string; weight: number }[], - weightBasename: 3, - weightDirectory: 2, - weightH1: 1.5, - weightH2: 1.3, - weightH3: 1.1, - weightUnmarkedTags: 1.1, - weightCustomProperties: [] as { name: string; weight: number }[], + httpApiEnabled: false, + httpApiPort: '51361', + httpApiNotice: true, - httpApiEnabled: false, - httpApiPort: '51361', - httpApiNotice: true, + welcomeMessage: '', + verboseLogging: false, + } +} - welcomeMessage: '', - verboseLogging: false, -} as const +let settings: OmnisearchSettings -export let settings = Object.assign({}, DEFAULT_SETTINGS) as OmnisearchSettings +export function getSettings(): OmnisearchSettings { + if (!settings) { + settings = Object.assign({}, getDefaultSettings()) as OmnisearchSettings + } + return settings +} export async function loadSettings(plugin: Plugin): Promise { - settings = Object.assign({}, DEFAULT_SETTINGS, await plugin.loadData()) + settings = Object.assign({}, getDefaultSettings(), await plugin.loadData()) showExcerpt.set(settings.showExcerpt) } @@ -766,13 +769,17 @@ export async function saveSettings(plugin: Plugin): Promise { } export function isPluginDisabled(): boolean { - return app.loadLocalStorage(K_DISABLE_OMNISEARCH) === '1' + return getObsidianApp().loadLocalStorage(K_DISABLE_OMNISEARCH) === '1' } export function canIndexUnsupportedFiles(): boolean { return ( settings.unsupportedFilesIndexing === 'yes' || (settings.unsupportedFilesIndexing === 'default' && - !!app.vault.getConfig('showUnsupportedFiles')) + !!getObsidianApp().vault.getConfig('showUnsupportedFiles')) ) } + +export function isCacheEnabled(): boolean { + return !Platform.isIosApp && settings.useCache +} diff --git a/src/stores/obsidian-app.ts b/src/stores/obsidian-app.ts index 27885c3..f9984d5 100644 --- a/src/stores/obsidian-app.ts +++ b/src/stores/obsidian-app.ts @@ -11,9 +11,7 @@ export function setObsidianApp(app: App) { */ export function getObsidianApp() { if (!obsidianApp) { - // throw new Error('Obsidian app not set') - // console.trace('Obsidian app not set') - return app // FIXME: please. + throw new Error('Obsidian app not set') } return obsidianApp as App } diff --git a/src/tools/api-server.ts b/src/tools/api-server.ts index b49e6aa..8d0da04 100644 --- a/src/tools/api-server.ts +++ b/src/tools/api-server.ts @@ -2,7 +2,7 @@ import * as http from 'http' import * as url from 'url' import api from './api' import { Notice } from 'obsidian' -import { settings } from 'src/settings' +import { getSettings } from 'src/settings' export function getServer() { const server = http.createServer(async function (req, res) { @@ -47,7 +47,7 @@ export function getServer() { }, () => { console.log(`Omnisearch - Started HTTP server on port ${port}`) - if (settings.httpApiNotice) { + if (getSettings().httpApiNotice) { new Notice(`Omnisearch - Started HTTP server on port ${port}`) } } @@ -62,6 +62,7 @@ export function getServer() { }, close() { server.close() + const settings = getSettings() console.log(`Omnisearch - Terminated HTTP server`) if (settings.httpApiEnabled && settings.httpApiNotice) { new Notice(`Omnisearch - Terminated HTTP server`) diff --git a/src/tools/api.ts b/src/tools/api.ts index 7b7d4dc..d5d8b9c 100644 --- a/src/tools/api.ts +++ b/src/tools/api.ts @@ -1,9 +1,9 @@ import type { ResultNote } from '../globals' import { Query } from '../search/query' -import { searchEngine } from '../search/omnisearch' import { makeExcerpt } from './text-processing' import { refreshIndex } from '../notes-index' import { getObsidianApp } from '../stores/obsidian-app' +import { Omnisearch } from 'src/search/omnisearch' type ResultNoteApi = { score: number @@ -20,7 +20,6 @@ export type SearchMatchApi = { offset: number } -const app = getObsidianApp() let notified = false @@ -37,7 +36,7 @@ function mapResults(results: ResultNote[]): ResultNoteApi[] { const res: ResultNoteApi = { score, - vault: app.vault.getName(), + vault: getObsidianApp().vault.getName(), path, basename, foundWords, @@ -56,7 +55,7 @@ function mapResults(results: ResultNote[]): ResultNoteApi[] { async function search(q: string): Promise { const query = new Query(q) - const raw = await searchEngine.getSuggestions(query) + const raw = await Omnisearch.getInstance().getSuggestions(query) return mapResults(raw) } diff --git a/src/tools/notes.ts b/src/tools/notes.ts index 6081d60..2ab25b9 100644 --- a/src/tools/notes.ts +++ b/src/tools/notes.ts @@ -2,8 +2,6 @@ import { type CachedMetadata, MarkdownView, TFile } from 'obsidian' import type { ResultNote } from '../globals' import { getObsidianApp } from '../stores/obsidian-app' -const app = getObsidianApp() - export async function openNote( item: ResultNote, offset = 0, @@ -12,6 +10,7 @@ export async function openNote( ): Promise { // Check if the note is already open, // to avoid opening it twice if the first one is pinned + const app = getObsidianApp() let alreadyOpenAndPinned = false app.workspace.iterateAllLeaves(leaf => { if (leaf.view instanceof MarkdownView) { @@ -48,6 +47,7 @@ export async function openNote( } export async function createNote(name: string, newLeaf = false): Promise { + const app = getObsidianApp() try { let pathPrefix: string switch (app.vault.getConfig('newFileLocation')) { @@ -83,7 +83,7 @@ export function getNonExistingNotes( return (metadata.links ?? []) .map(l => { const path = removeAnchors(l.link) - return app.metadataCache.getFirstLinkpathDest(path, file.path) + return getObsidianApp().metadataCache.getFirstLinkpathDest(path, file.path) ? '' : l.link }) diff --git a/src/tools/text-processing.ts b/src/tools/text-processing.ts index 481ea3d..ee1816e 100644 --- a/src/tools/text-processing.ts +++ b/src/tools/text-processing.ts @@ -1,5 +1,4 @@ import { - highlightClass, type SearchMatch, regexLineSplit, regexYaml, @@ -7,11 +6,11 @@ import { excerptAfter, excerptBefore, } from 'src/globals' -import { settings } from 'src/settings' import { removeDiacritics, warnDebug } from './utils' import type { Query } from 'src/search/query' import { Notice } from 'obsidian' import { escapeRegExp } from 'lodash-es' +import { getSettings } from 'src/settings' /** * Wraps the matches in the text with a element and a highlight class @@ -20,6 +19,10 @@ import { escapeRegExp } from 'lodash-es' * @returns The html string with the matches highlighted */ export function highlightText(text: string, matches: SearchMatch[]): string { + const highlightClass = `suggestion-highlight omnisearch-highlight ${ + getSettings().highlight ? 'omnisearch-default-highlight' : '' + }` + if (!matches.length) { return text } @@ -125,7 +128,7 @@ export function getMatches( ): SearchMatch[] { const originalText = text // text = text.toLowerCase().replace(new RegExp(SEPARATORS, 'gu'), ' ') - if (settings.ignoreDiacritics) { + if (getSettings().ignoreDiacritics) { text = removeDiacritics(text) } const startTime = new Date().getTime() @@ -165,6 +168,7 @@ export function getMatches( } export function makeExcerpt(content: string, offset: number): string { + const settings = getSettings() try { const pos = offset ?? -1 const from = Math.max(0, pos - excerptBefore) diff --git a/src/tools/utils.ts b/src/tools/utils.ts index d2f66dd..a3c469f 100644 --- a/src/tools/utils.ts +++ b/src/tools/utils.ts @@ -5,7 +5,7 @@ import { Platform, } from 'obsidian' import { getTextExtractor, isSearchMatch, type SearchMatch } from '../globals' -import { canIndexUnsupportedFiles, settings } from '../settings' +import { canIndexUnsupportedFiles, getSettings } from '../settings' import { type BinaryLike, createHash } from 'crypto' import { md5 } from 'pure-md5' @@ -136,6 +136,7 @@ export function getCtrlKeyLabel(): 'ctrl' | '⌘' { } export function isContentIndexable(path: string): boolean { + const settings = getSettings() const hasTextExtractor = !!getTextExtractor() const canIndexPDF = hasTextExtractor && settings.PDFIndexing const canIndexImages = hasTextExtractor && settings.imagesIndexing @@ -176,7 +177,7 @@ export function isFileOffice(path: string): boolean { } export function isFilePlaintext(path: string): boolean { - return [...settings.indexedFileTypes, 'md'].some(t => path.endsWith(`.${t}`)) + return [...getSettings().indexedFileTypes, 'md'].some(t => path.endsWith(`.${t}`)) } export function isFileCanvas(path: string): boolean { @@ -251,7 +252,7 @@ export function warnDebug(...args: any[]): void { } function printDebug(fn: (...args: any[]) => any, ...args: any[]): void { - if (settings.verboseLogging) { + if (getSettings().verboseLogging) { const t = new Date() const ts = `${t.getMinutes()}:${t.getSeconds()}:${t.getMilliseconds()}` fn(...['Omnisearch -', ts + ' -', ...args])