@difizen/libro-search 0.0.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/es/abstract-search-provider.d.ts +113 -0
  4. package/es/abstract-search-provider.d.ts.map +1 -0
  5. package/es/abstract-search-provider.js +126 -0
  6. package/es/index.d.ts +10 -0
  7. package/es/index.d.ts.map +1 -0
  8. package/es/index.js +9 -0
  9. package/es/index.less +107 -0
  10. package/es/libro-cell-search-provider.d.ts +10 -0
  11. package/es/libro-cell-search-provider.d.ts.map +1 -0
  12. package/es/libro-cell-search-provider.js +54 -0
  13. package/es/libro-search-engine-html.d.ts +3 -0
  14. package/es/libro-search-engine-html.d.ts.map +1 -0
  15. package/es/libro-search-engine-html.js +59 -0
  16. package/es/libro-search-engine-text.d.ts +10 -0
  17. package/es/libro-search-engine-text.d.ts.map +1 -0
  18. package/es/libro-search-engine-text.js +32 -0
  19. package/es/libro-search-generic-provider.d.ts +107 -0
  20. package/es/libro-search-generic-provider.d.ts.map +1 -0
  21. package/es/libro-search-generic-provider.js +467 -0
  22. package/es/libro-search-manager.d.ts +22 -0
  23. package/es/libro-search-manager.d.ts.map +1 -0
  24. package/es/libro-search-manager.js +102 -0
  25. package/es/libro-search-model.d.ts +111 -0
  26. package/es/libro-search-model.d.ts.map +1 -0
  27. package/es/libro-search-model.js +395 -0
  28. package/es/libro-search-protocol.d.ts +176 -0
  29. package/es/libro-search-protocol.d.ts.map +1 -0
  30. package/es/libro-search-protocol.js +36 -0
  31. package/es/libro-search-provider.d.ts +138 -0
  32. package/es/libro-search-provider.d.ts.map +1 -0
  33. package/es/libro-search-provider.js +759 -0
  34. package/es/libro-search-utils.d.ts +25 -0
  35. package/es/libro-search-utils.d.ts.map +1 -0
  36. package/es/libro-search-utils.js +85 -0
  37. package/es/libro-search-view.d.ts +59 -0
  38. package/es/libro-search-view.d.ts.map +1 -0
  39. package/es/libro-search-view.js +455 -0
  40. package/es/module.d.ts +4 -0
  41. package/es/module.d.ts.map +1 -0
  42. package/es/module.js +35 -0
  43. package/package.json +66 -0
  44. package/src/abstract-search-provider.ts +160 -0
  45. package/src/index.less +107 -0
  46. package/src/index.ts +9 -0
  47. package/src/libro-cell-search-provider.ts +39 -0
  48. package/src/libro-search-engine-html.ts +74 -0
  49. package/src/libro-search-engine-text.ts +34 -0
  50. package/src/libro-search-generic-provider.ts +303 -0
  51. package/src/libro-search-manager.ts +86 -0
  52. package/src/libro-search-model.ts +266 -0
  53. package/src/libro-search-protocol.ts +209 -0
  54. package/src/libro-search-provider.ts +507 -0
  55. package/src/libro-search-utils.spec.ts +37 -0
  56. package/src/libro-search-utils.ts +83 -0
  57. package/src/libro-search-view.tsx +404 -0
  58. package/src/module.ts +59 -0
@@ -0,0 +1,303 @@
1
+ /* eslint-disable @typescript-eslint/no-loop-func */
2
+ /* eslint-disable @typescript-eslint/no-unused-vars */
3
+ import type { View } from '@difizen/mana-app';
4
+ import { prop } from '@difizen/mana-app';
5
+ import { inject, transient } from '@difizen/mana-app';
6
+
7
+ import { AbstractSearchProvider } from './abstract-search-provider.js';
8
+ import { searchInHTML } from './libro-search-engine-html.js';
9
+ import type { HTMLSearchMatch } from './libro-search-protocol.js';
10
+ import {
11
+ LIBRO_SEARCH_FOUND_CLASSES,
12
+ LIBRO_SEARCH_SELECTED_CLASSES,
13
+ SearchProviderOption,
14
+ } from './libro-search-protocol.js';
15
+
16
+ export type GenericSearchProviderFactory = (
17
+ option: SearchProviderOption,
18
+ ) => GenericSearchProvider;
19
+ export const GenericSearchProviderFactory = Symbol('GenericSearchProviderFactory');
20
+ /**
21
+ * Generic DOM tree search provider.
22
+ */
23
+ @transient()
24
+ export class GenericSearchProvider extends AbstractSearchProvider {
25
+ protected _query: RegExp | null;
26
+ protected _currentMatchIndex: number;
27
+ @prop() protected _matches: HTMLSearchMatch[] = [];
28
+ protected _mutationObserver: MutationObserver = new MutationObserver(
29
+ this._onWidgetChanged.bind(this),
30
+ );
31
+ protected _markNodes = new Array<HTMLSpanElement>();
32
+ /**
33
+ * Report whether or not this provider has the ability to search on the given object
34
+ */
35
+ static isApplicable(domain: View): boolean {
36
+ return !!domain.container?.current;
37
+ }
38
+
39
+ /**
40
+ * The current index of the selected match.
41
+ */
42
+ override get currentMatchIndex(): number | undefined {
43
+ return this._currentMatchIndex >= 0 ? this._currentMatchIndex : undefined;
44
+ }
45
+
46
+ /**
47
+ * The current match
48
+ */
49
+ get currentMatch(): HTMLSearchMatch | undefined {
50
+ return this._matches[this._currentMatchIndex] ?? undefined;
51
+ }
52
+
53
+ /**
54
+ * The current matches
55
+ */
56
+ get matches(): HTMLSearchMatch[] {
57
+ // Ensure that no other fn can overwrite matches index property
58
+ // We shallow clone each node
59
+ return this._matches
60
+ ? this._matches.map((m) => Object.assign({}, m))
61
+ : this._matches;
62
+ }
63
+
64
+ /**
65
+ * The number of matches.
66
+ */
67
+ override get matchesCount(): number | undefined {
68
+ return this._matches.length;
69
+ }
70
+
71
+ /**
72
+ * Set to true if the widget under search is read-only, false
73
+ * if it is editable. Will be used to determine whether to show
74
+ * the replace option.
75
+ */
76
+ readonly isReadOnly = true;
77
+
78
+ constructor(@inject(SearchProviderOption) option: SearchProviderOption) {
79
+ super(option);
80
+ }
81
+ /**
82
+ * Clear currently highlighted match.
83
+ */
84
+ clearHighlight(): Promise<void> {
85
+ if (this._currentMatchIndex >= 0) {
86
+ const hit = this._markNodes[this._currentMatchIndex];
87
+ hit.classList.remove(...LIBRO_SEARCH_SELECTED_CLASSES);
88
+ }
89
+ this._currentMatchIndex = -1;
90
+
91
+ return Promise.resolve();
92
+ }
93
+
94
+ /**
95
+ * Dispose of the resources held by the search provider.
96
+ *
97
+ * #### Notes
98
+ * If the object's `dispose` method is called more than once, all
99
+ * calls made after the first will be a no-op.
100
+ *
101
+ * #### Undefined Behavior
102
+ * It is undefined behavior to use any functionality of the object
103
+ * after it has been disposed unless otherwise explicitly noted.
104
+ */
105
+ override dispose(): void {
106
+ if (this.isDisposed) {
107
+ return;
108
+ }
109
+
110
+ this.endQuery().catch((reason) => {
111
+ console.error(`Failed to end search query.`, reason);
112
+ });
113
+ super.dispose();
114
+ }
115
+
116
+ /**
117
+ * Move the current match indicator to the next match.
118
+ *
119
+ * @param loop Whether to loop within the matches list.
120
+ *
121
+ * @returns A promise that resolves once the action has completed.
122
+ */
123
+ async highlightNext(loop?: boolean): Promise<HTMLSearchMatch | undefined> {
124
+ return this._highlightNext(false, loop ?? true) ?? undefined;
125
+ }
126
+
127
+ /**
128
+ * Move the current match indicator to the previous match.
129
+ *
130
+ * @param loop Whether to loop within the matches list.
131
+ *
132
+ * @returns A promise that resolves once the action has completed.
133
+ */
134
+ async highlightPrevious(loop?: boolean): Promise<HTMLSearchMatch | undefined> {
135
+ return this._highlightNext(true, loop ?? true) ?? undefined;
136
+ }
137
+
138
+ /**
139
+ * Replace the currently selected match with the provided text
140
+ *
141
+ * @param newText The replacement text
142
+ * @param loop Whether to loop within the matches list.
143
+ *
144
+ * @returns A promise that resolves with a boolean indicating whether a replace occurred.
145
+ */
146
+ async replaceCurrentMatch(_newText: string, _loop?: boolean): Promise<boolean> {
147
+ return Promise.resolve(false);
148
+ }
149
+
150
+ /**
151
+ * Replace all matches in the notebook with the provided text
152
+ *
153
+ * @param newText The replacement text
154
+ *
155
+ * @returns A promise that resolves with a boolean indicating whether a replace occurred.
156
+ */
157
+ async replaceAllMatches(_newText: string): Promise<boolean> {
158
+ // This is read only, but we could loosen this in theory for input boxes...
159
+ return Promise.resolve(false);
160
+ }
161
+
162
+ /**
163
+ * Initialize the search using the provided options. Should update the UI
164
+ * to highlight all matches and "select" whatever the first match should be.
165
+ *
166
+ * @param query A RegExp to be use to perform the search
167
+ * @param filters Filter parameters to pass to provider
168
+ */
169
+ startQuery = async (query: RegExp | null, _filters = {}): Promise<void> => {
170
+ await this.endQuery();
171
+ this._query = query;
172
+
173
+ if (query === null) {
174
+ return Promise.resolve();
175
+ }
176
+
177
+ const matches = this.view.container?.current
178
+ ? await searchInHTML(query, this.view.container?.current)
179
+ : [];
180
+
181
+ // Transform the DOM
182
+ let nodeIdx = 0;
183
+ while (nodeIdx < matches.length) {
184
+ const activeNode = matches[nodeIdx].node;
185
+ const parent = activeNode.parentNode!;
186
+
187
+ const subMatches = [matches[nodeIdx]];
188
+ while (++nodeIdx < matches.length && matches[nodeIdx].node === activeNode) {
189
+ subMatches.unshift(matches[nodeIdx]);
190
+ }
191
+
192
+ const markedNodes = subMatches.map((match) => {
193
+ // TODO: support tspan for svg when svg support is added
194
+ const markedNode = document.createElement('mark');
195
+ markedNode.classList.add(...LIBRO_SEARCH_FOUND_CLASSES);
196
+ markedNode.textContent = match.text;
197
+
198
+ const newNode = activeNode.splitText(match.position);
199
+ newNode.textContent = newNode.textContent!.slice(match.text.length);
200
+ parent.insertBefore(markedNode, newNode);
201
+ return markedNode;
202
+ });
203
+
204
+ // Insert node in reverse order as we replace from last to first
205
+ // to maintain match position.
206
+ for (let i = markedNodes.length - 1; i >= 0; i--) {
207
+ this._markNodes.push(markedNodes[i]);
208
+ }
209
+ }
210
+ if (this.view.container?.current) {
211
+ // Watch for future changes:
212
+ this._mutationObserver.observe(
213
+ this.view.container?.current,
214
+ // https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit
215
+ {
216
+ attributes: false,
217
+ characterData: true,
218
+ childList: true,
219
+ subtree: true,
220
+ },
221
+ );
222
+ }
223
+ this._matches = matches;
224
+ };
225
+
226
+ /**
227
+ * Clear the highlighted matches and any internal state.
228
+ */
229
+ async endQuery(): Promise<void> {
230
+ this._mutationObserver.disconnect();
231
+ this._markNodes.forEach((el) => {
232
+ const parent = el.parentNode!;
233
+ parent.replaceChild(document.createTextNode(el.textContent!), el);
234
+ parent.normalize();
235
+ });
236
+ this._markNodes = [];
237
+ this._matches = [];
238
+ this._currentMatchIndex = -1;
239
+ }
240
+
241
+ protected _highlightNext(reverse: boolean, loop: boolean): HTMLSearchMatch | null {
242
+ if (this._matches.length === 0) {
243
+ return null;
244
+ }
245
+ if (this._currentMatchIndex === -1) {
246
+ this._currentMatchIndex = reverse ? this.matches.length - 1 : 0;
247
+ } else {
248
+ const hit = this._markNodes[this._currentMatchIndex];
249
+ hit.classList.remove(...LIBRO_SEARCH_SELECTED_CLASSES);
250
+
251
+ this._currentMatchIndex = reverse
252
+ ? this._currentMatchIndex - 1
253
+ : this._currentMatchIndex + 1;
254
+ if (
255
+ loop &&
256
+ (this._currentMatchIndex < 0 || this._currentMatchIndex >= this._matches.length)
257
+ ) {
258
+ // Cheap way to make this a circular buffer
259
+ this._currentMatchIndex =
260
+ (this._currentMatchIndex + this._matches.length) % this._matches.length;
261
+ }
262
+ }
263
+
264
+ if (
265
+ this._currentMatchIndex >= 0 &&
266
+ this._currentMatchIndex < this._matches.length
267
+ ) {
268
+ const hit = this._markNodes[this._currentMatchIndex];
269
+ hit.classList.add(...LIBRO_SEARCH_SELECTED_CLASSES);
270
+ // If not in view, scroll just enough to see it
271
+ if (!elementInViewport(hit)) {
272
+ hit.scrollIntoView(reverse);
273
+ }
274
+ hit.focus();
275
+
276
+ return this._matches[this._currentMatchIndex];
277
+ } else {
278
+ this._currentMatchIndex = -1;
279
+ return null;
280
+ }
281
+ }
282
+
283
+ protected async _onWidgetChanged(
284
+ _mutations: MutationRecord[],
285
+ _observer: MutationObserver,
286
+ ) {
287
+ this._currentMatchIndex = -1;
288
+ // This is typically cheap, but we do not control the rate of change or size of the output
289
+ await this.startQuery(this._query);
290
+ this._stateChanged.fire();
291
+ }
292
+ }
293
+ function elementInViewport(el: HTMLElement): boolean {
294
+ const boundingClientRect = el.getBoundingClientRect();
295
+ return (
296
+ boundingClientRect.top >= 0 &&
297
+ boundingClientRect.bottom <=
298
+ (window.innerHeight || document.documentElement.clientHeight) &&
299
+ boundingClientRect.left >= 0 &&
300
+ boundingClientRect.right <=
301
+ (window.innerWidth || document.documentElement.clientWidth)
302
+ );
303
+ }
@@ -0,0 +1,86 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import {
3
+ LibroCommandRegister,
4
+ LibroExtensionSlotContribution,
5
+ } from '@difizen/libro-core';
6
+ import type {
7
+ LibroExtensionSlotFactory,
8
+ LibroSlot,
9
+ LibroView,
10
+ } from '@difizen/libro-core';
11
+ import type { CommandRegistry, KeybindingRegistry } from '@difizen/mana-app';
12
+ import {
13
+ ViewManager,
14
+ CommandContribution,
15
+ KeybindingContribution,
16
+ } from '@difizen/mana-app';
17
+ import { inject, singleton } from '@difizen/mana-app';
18
+
19
+ import { LibroSearchView } from './libro-search-view.js';
20
+
21
+ export const LibroSearchToggleCommand = {
22
+ ShowLibroSearch: {
23
+ id: 'libro-search:toggle',
24
+ keybind: 'ctrlcmd+F',
25
+ },
26
+ // LibroSearchToggle: {
27
+ // id: 'libro-search:toogle',
28
+ // keybind: 'ctrlcmd+F',
29
+ // },
30
+ };
31
+
32
+ @singleton({
33
+ contrib: [
34
+ CommandContribution,
35
+ KeybindingContribution,
36
+ LibroExtensionSlotContribution,
37
+ ],
38
+ })
39
+ export class LibroSearchManager
40
+ implements
41
+ CommandContribution,
42
+ KeybindingContribution,
43
+ LibroExtensionSlotContribution
44
+ {
45
+ @inject(ViewManager) viewManager: ViewManager;
46
+ @inject(LibroCommandRegister) libroCommandRegister: LibroCommandRegister;
47
+ protected viewMap: Map<string, LibroSearchView> = new Map();
48
+
49
+ public readonly slot: LibroSlot = 'container';
50
+ registerKeybindings(keybindings: KeybindingRegistry): void {
51
+ this.libroCommandRegister.registerKeybinds(
52
+ keybindings,
53
+ LibroSearchToggleCommand,
54
+ false,
55
+ false,
56
+ );
57
+ }
58
+ registerCommands(commands: CommandRegistry) {
59
+ this.libroCommandRegister.registerLibroCommand(
60
+ commands,
61
+ LibroSearchToggleCommand.ShowLibroSearch,
62
+ {
63
+ execute: (_cell, libro, _position) => {
64
+ if (libro) {
65
+ this.showSearchView(libro);
66
+ }
67
+ },
68
+ },
69
+ );
70
+ }
71
+ factory: LibroExtensionSlotFactory = async (libro: LibroView) => {
72
+ const view = await this.viewManager.getOrCreateView(LibroSearchView, {
73
+ parentId: libro.id,
74
+ });
75
+ view.libro = libro;
76
+ this.viewMap.set(libro.id, view);
77
+ view.onDisposed(() => {
78
+ this.viewMap.delete(libro.id);
79
+ });
80
+ return view;
81
+ };
82
+
83
+ showSearchView = (libro: LibroView) => {
84
+ this.viewMap.get(libro.id)?.show();
85
+ };
86
+ }
@@ -0,0 +1,266 @@
1
+ import type { Disposable } from '@difizen/mana-app';
2
+ import { DisposableCollection, Emitter } from '@difizen/mana-app';
3
+ import { inject, singleton } from '@difizen/mana-app';
4
+ import debounce from 'lodash.debounce';
5
+
6
+ import type { SearchProvider } from './libro-search-protocol.js';
7
+ import type { SearchFilter, SearchFilters } from './libro-search-protocol.js';
8
+ import { LibroSearchUtils } from './libro-search-utils.js';
9
+
10
+ /**
11
+ * Search in a document model.
12
+ */
13
+ @singleton()
14
+ export class LibroSearchModel implements Disposable {
15
+ utils: LibroSearchUtils;
16
+ protected _disposed?: boolean = false;
17
+ protected _caseSensitive = false;
18
+ protected parsingError = '';
19
+ protected _filters: SearchFilters = {
20
+ searchCellOutput: true,
21
+ onlySearchSelectedCells: false,
22
+ };
23
+ protected _replaceText = '';
24
+ protected searchDebouncer: any;
25
+ protected _searchExpression = '';
26
+ protected _useRegex = false;
27
+ protected disposedEmitter = new Emitter<void>();
28
+ protected searchProvider: SearchProvider;
29
+ protected toDispose = new DisposableCollection();
30
+ get onDisposed() {
31
+ return this.disposedEmitter.event;
32
+ }
33
+
34
+ get disposed() {
35
+ return !!this._disposed;
36
+ }
37
+ /**
38
+ * Search document model
39
+ * @param searchProvider Provider for the current document
40
+ * @param searchDebounceTime Debounce search time
41
+ */
42
+ constructor(
43
+ @inject(LibroSearchUtils) utils: LibroSearchUtils,
44
+ searchProvider: SearchProvider,
45
+ searchDebounceTime: number,
46
+ ) {
47
+ this.utils = utils;
48
+ this.searchProvider = searchProvider;
49
+ // this._filters = {};
50
+ // if (this.searchProvider.getFilters) {
51
+ // const filters = this.searchProvider.getFilters();
52
+ // for (const filter in filters) {
53
+ // this._filters[filter] = filters[filter].default;
54
+ // }
55
+ // }
56
+
57
+ this.toDispose.push(searchProvider.stateChanged(this.refresh));
58
+
59
+ this.searchDebouncer = debounce(() => {
60
+ this.updateSearch().catch((reason) => {
61
+ console.error('Failed to update search on document.', reason);
62
+ });
63
+ }, searchDebounceTime);
64
+ }
65
+
66
+ /**
67
+ * Whether the search is case sensitive or not.
68
+ */
69
+ get caseSensitive(): boolean {
70
+ return this._caseSensitive;
71
+ }
72
+ set caseSensitive(v: boolean) {
73
+ if (this._caseSensitive !== v) {
74
+ this._caseSensitive = v;
75
+ this.refresh();
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Current highlighted match index.
81
+ */
82
+ get currentIndex(): number | undefined {
83
+ return this.searchProvider.currentMatchIndex;
84
+ }
85
+
86
+ /**
87
+ * Filter values.
88
+ */
89
+ get filters(): SearchFilters {
90
+ return this._filters;
91
+ }
92
+
93
+ /**
94
+ * Filter definitions for the current provider.
95
+ */
96
+ get filtersDefinition(): Record<string, SearchFilter> {
97
+ return this.searchProvider.getFilters?.() ?? {};
98
+ }
99
+
100
+ /**
101
+ * The initial query string.
102
+ */
103
+ get initialQuery(): string {
104
+ if (!this.searchProvider.getInitialQuery) {
105
+ return this._searchExpression;
106
+ }
107
+ return this._searchExpression || this.searchProvider.getInitialQuery();
108
+ }
109
+
110
+ /**
111
+ * Whether the document is read-only or not.
112
+ */
113
+ get isReadOnly(): boolean {
114
+ return this.searchProvider.isReadOnly;
115
+ }
116
+
117
+ /**
118
+ * Replacement expression
119
+ */
120
+ get replaceText(): string {
121
+ return this._replaceText;
122
+ }
123
+ set replaceText(v: string) {
124
+ if (this._replaceText !== v) {
125
+ this._replaceText = v;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Search expression
131
+ */
132
+ get searchExpression(): string {
133
+ return this._searchExpression;
134
+ }
135
+ set searchExpression(v: string) {
136
+ if (this._searchExpression !== v) {
137
+ this._searchExpression = v;
138
+ this.refresh();
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Total number of matches.
144
+ */
145
+ get totalMatches(): number | undefined {
146
+ return this.searchProvider.matchesCount;
147
+ }
148
+
149
+ /**
150
+ * Whether to use regular expression or not.
151
+ */
152
+ get useRegex(): boolean {
153
+ return this._useRegex;
154
+ }
155
+ set useRegex(v: boolean) {
156
+ if (this._useRegex !== v) {
157
+ this._useRegex = v;
158
+ this.refresh();
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Dispose the model.
164
+ */
165
+ dispose(): void {
166
+ if (this.disposed) {
167
+ return;
168
+ }
169
+ if (this._searchExpression) {
170
+ this.endQuery().catch((reason) => {
171
+ console.error(`Failed to end query '${this._searchExpression}.`, reason);
172
+ });
173
+ }
174
+ this.toDispose.dispose();
175
+ this.searchDebouncer.dispose();
176
+ this._disposed = true;
177
+ }
178
+
179
+ /**
180
+ * End the query.
181
+ */
182
+ async endQuery(): Promise<void> {
183
+ await this.searchProvider.endQuery();
184
+ }
185
+
186
+ /**
187
+ * Highlight the next match.
188
+ */
189
+ async highlightNext(): Promise<void> {
190
+ await this.searchProvider.highlightNext();
191
+ }
192
+
193
+ /**
194
+ * Highlight the previous match
195
+ */
196
+ async highlightPrevious(): Promise<void> {
197
+ await this.searchProvider.highlightPrevious();
198
+ }
199
+
200
+ /**
201
+ * Refresh search
202
+ */
203
+ refresh(): void {
204
+ this.searchDebouncer.invoke().catch((reason: any) => {
205
+ console.error('Failed to invoke search document debouncer.', reason);
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Replace all matches.
211
+ */
212
+ async replaceAllMatches(): Promise<void> {
213
+ await this.searchProvider.replaceAllMatches(this._replaceText);
214
+ // Emit state change as the index needs to be updated
215
+ }
216
+
217
+ /**
218
+ * Replace the current match.
219
+ */
220
+ async replaceCurrentMatch(): Promise<void> {
221
+ await this.searchProvider.replaceCurrentMatch(this._replaceText);
222
+ }
223
+
224
+ /**
225
+ * Set the value of a given filter.
226
+ *
227
+ * @param name Filter name
228
+ * @param v Filter value
229
+ */
230
+ async setFilter(_name: string, _v: boolean): Promise<void> {
231
+ // if (this._filters[name] !== v) {
232
+ // if (this.searchProvider.validateFilter) {
233
+ // this._filters[name] = await this.searchProvider.validateFilter(name, v);
234
+ // // If the value was changed
235
+ // if (this._filters[name] === v) {
236
+ // this.refresh();
237
+ // }
238
+ // } else {
239
+ // this._filters[name] = v;
240
+ // this.refresh();
241
+ // }
242
+ // }
243
+ }
244
+
245
+ protected async updateSearch(): Promise<void> {
246
+ if (this.parsingError) {
247
+ this.parsingError = '';
248
+ }
249
+ try {
250
+ const query = this.searchExpression
251
+ ? this.utils.parseQuery(
252
+ this.searchExpression,
253
+ this.caseSensitive,
254
+ this.useRegex,
255
+ )
256
+ : null;
257
+ if (query) {
258
+ await this.searchProvider.startQuery(query, this._filters);
259
+ // Emit state change as the index needs to be updated
260
+ }
261
+ } catch (reason: any) {
262
+ this.parsingError = reason.toString();
263
+ console.error(`Failed to parse expression ${this.searchExpression}`, reason);
264
+ }
265
+ }
266
+ }