From 23640f4b0b09635e4d3870a2e8893b0e9912bbc2 Mon Sep 17 00:00:00 2001 From: Simon Cambier Date: Sun, 12 Mar 2023 11:30:56 +0100 Subject: [PATCH] #208 - Added a time limits for regex matchers --- src/search/omnisearch.ts | 12 +++++++++--- src/tools/utils.ts | 14 ++++++++++++-- src/vendor/parse-query.ts | 25 ++++++++++++++++--------- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/search/omnisearch.ts b/src/search/omnisearch.ts index a7d7eb4..0a06b8f 100644 --- a/src/search/omnisearch.ts +++ b/src/search/omnisearch.ts @@ -14,6 +14,7 @@ import { splitCamelCase, stringsToRegex, stripMarkdownCharacters, + warnDebug, } from '../tools/utils' import { Notice } from 'obsidian' import type { Query } from './query' @@ -144,11 +145,11 @@ export class Omnisearch { logDebug('Indexing into search engine', docs) // Update the list of indexed docs docs.forEach(doc => this.indexedDocuments.set(doc.path, doc.mtime)) - + // Discard files that may have been already added (though it shouldn't happen) const alreadyAdded = docs.filter(doc => this.minisearch.has(doc.path)) this.removeFromPaths(alreadyAdded.map(o => o.path)) - + // Add docs to minisearch await this.minisearch.addAllAsync(docs) } @@ -304,11 +305,16 @@ export class Omnisearch { } public getMatches(text: string, reg: RegExp, query: Query): SearchMatch[] { + const startTime = new Date().getTime() let match: RegExpExecArray | null = null const matches: SearchMatch[] = [] let count = 0 while ((match = reg.exec(text)) !== null) { - if (++count >= 100) break // Avoid infinite loops, stop looking after 100 matches + // Avoid infinite loops, stop looking after 100 matches or if we're taking too much time + if (++count >= 100 || new Date().getTime() - startTime > 50) { + warnDebug('Stopped getMatches at', count, 'results') + break + } const m = match[0] if (m) matches.push({ match: m, offset: match.index }) } diff --git a/src/tools/utils.ts b/src/tools/utils.ts index 6d160d0..8ab8131 100644 --- a/src/tools/utils.ts +++ b/src/tools/utils.ts @@ -325,8 +325,18 @@ export function splitCamelCase(text: string): string[] { return text.replace(/([a-z](?=[A-Z]))/g, '$1 ').split(' ') } -export function logDebug(...attr: any[]): void { +export function logDebug(...args: any[]): void { + printDebug(console.log, ...args) +} + +export function warnDebug(...args: any[]): void { + printDebug(console.warn, ...args) +} + +function printDebug(fn: (...args: any[]) => any, ...args: any[]): void { if (settings.verboseLogging) { - console.log(...['Omnisearch -', ...attr]) + const t = new Date() + const ts = `${t.getMinutes()}:${t.getSeconds()}:${t.getMilliseconds()}` + fn(...['Omnisearch -', ts + ' -', ...args]) } } diff --git a/src/vendor/parse-query.ts b/src/vendor/parse-query.ts index 0679133..a0c846a 100644 --- a/src/vendor/parse-query.ts +++ b/src/vendor/parse-query.ts @@ -6,6 +6,8 @@ * MIT Licensed */ +import { warnDebug } from "../tools/utils"; + interface SearchParserOptions { offsets?: boolean tokenize: true @@ -30,7 +32,7 @@ type SearchParserTextOffset = { type SearchParserOffset = ( | SearchParserKeyWordOffset | SearchParserTextOffset -) & { + ) & { offsetStart: number offsetEnd: number } @@ -43,7 +45,7 @@ interface SearchParserResult extends ISearchParserDictionary { export function parseQuery( string: string, - options: SearchParserOptions + options: SearchParserOptions, ): SearchParserResult { // Set a default options object when none is provided if (!options) { @@ -74,9 +76,14 @@ export function parseQuery( const regex = /(\S+:'(?:[^'\\]|\\.)*')|(\S+:"(?:[^"\\]|\\.)*")|(-?"(?:[^"\\]|\\.)*")|(-?'(?:[^'\\]|\\.)*')|\S+|\S+:\S+/g let match - let count = 0 // TODO: FIXME: this is a hack to avoid infinite loops + let count = 0 + const startTime = new Date().getTime() + while ((match = regex.exec(string)) !== null) { - if (++count >= 100) break + if (++count >= 100 || new Date().getTime() - startTime > 50) { + warnDebug('Stopped SearchParserResult at', count, 'results') + break + } let term = match[0] const sepIndex = term.indexOf(':') @@ -291,11 +298,11 @@ export function parseQuery( query[key].from = rangeValues[0] query[key].to = rangeValues[1] } - // When pairs of ranges are specified - // keyword:XXXX-YYYY,AAAA-BBBB - // else if (!rangeValues.length % 2) { - // } - // When only getting a single value, + // When pairs of ranges are specified + // keyword:XXXX-YYYY,AAAA-BBBB + // else if (!rangeValues.length % 2) { + // } + // When only getting a single value, // or an odd number of values else { query[key].from = value