From c0d18ff19ebd21e4d472f2628afab3346ce38fe1 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 12 Mar 2021 16:26:02 -0500 Subject: [PATCH] Cleanup SearchStore (#2299) --- src/common/__tests__/search-store.test.ts | 12 +-- src/common/search-store.ts | 103 ++++++++++++---------- src/renderer/components/dock/log-list.tsx | 4 +- src/renderer/components/dock/log.store.ts | 12 +-- 4 files changed, 70 insertions(+), 61 deletions(-) diff --git a/src/common/__tests__/search-store.test.ts b/src/common/__tests__/search-store.test.ts index d361c858fd..27d52971de 100644 --- a/src/common/__tests__/search-store.test.ts +++ b/src/common/__tests__/search-store.test.ts @@ -29,28 +29,28 @@ describe("search store tests", () => { expect(searchStore.occurrences).toEqual([]); }); - it("find 3 occurences across 3 lines", () => { + it("find 3 occurrences across 3 lines", () => { searchStore.onSearch(logs, "172"); expect(searchStore.occurrences).toEqual([0, 1, 2]); }); - it("find occurences within 1 line (case-insensitive)", () => { + it("find occurrences within 1 line (case-insensitive)", () => { searchStore.onSearch(logs, "Starting"); expect(searchStore.occurrences).toEqual([2, 2]); }); - it("sets overlay index equal to first occurence", () => { + it("sets overlay index equal to first occurrence", () => { searchStore.onSearch(logs, "Replica"); expect(searchStore.activeOverlayIndex).toBe(0); }); - it("set overlay index to next occurence", () => { + it("set overlay index to next occurrence", () => { searchStore.onSearch(logs, "172"); searchStore.setNextOverlayActive(); expect(searchStore.activeOverlayIndex).toBe(1); }); - it("sets overlay to last occurence", () => { + it("sets overlay to last occurrence", () => { searchStore.onSearch(logs, "172"); searchStore.setPrevOverlayActive(); expect(searchStore.activeOverlayIndex).toBe(2); @@ -62,7 +62,7 @@ describe("search store tests", () => { }); it("escapes string for using in regex", () => { - const regex = searchStore.escapeRegex("some.interesting-query\\#?()[]"); + const regex = SearchStore.escapeRegex("some.interesting-query\\#?()[]"); expect(regex).toBe("some\\.interesting\\-query\\\\\\#\\?\\(\\)\\[\\]"); }); diff --git a/src/common/search-store.ts b/src/common/search-store.ts index 86a6054af3..c4e34ba5a2 100644 --- a/src/common/search-store.ts +++ b/src/common/search-store.ts @@ -3,9 +3,34 @@ import { dockStore } from "../renderer/components/dock/dock.store"; import { autobind } from "../renderer/utils"; export class SearchStore { - @observable searchQuery = ""; // Text in the search input - @observable occurrences: number[] = []; // Array with line numbers, eg [0, 0, 10, 21, 21, 40...] - @observable activeOverlayIndex = -1; // Index withing the occurences array. Showing where is activeOverlay currently located + /** + * An utility methods escaping user string to safely pass it into new Regex(variable) + * @param value Unescaped string + */ + public static escapeRegex(value?: string): string { + return value ? value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&") : ""; + } + + /** + * Text in the search input + * + * @observable + */ + @observable searchQuery = ""; + + /** + * Array with line numbers, eg [0, 0, 10, 21, 21, 40...] + * + * @observable + */ + @observable occurrences: number[] = []; + + /** + * Index within the occurrences array. Showing where is activeOverlay currently located + * + * @observable + */ + @observable activeOverlayIndex = -1; constructor() { reaction(() => dockStore.selectedTabId, () => { @@ -19,49 +44,45 @@ export class SearchStore { * @param query Search query from input */ @action - onSearch(text: string[], query = this.searchQuery) { + public onSearch(text?: string[] | null, query = this.searchQuery): void { this.searchQuery = query; if (!query) { - this.reset(); + return this.reset(); + } + this.occurrences = this.findOccurrences(text ?? [], query); + + if (!this.occurrences.length) { return; } - this.occurrences = this.findOccurences(text, query); - if (!this.occurrences.length) return; // If new highlighted keyword in exact same place as previous one, then no changing in active overlay - if (this.occurrences[this.activeOverlayIndex] !== undefined) return; - this.activeOverlayIndex = this.getNextOverlay(true); + if (this.occurrences[this.activeOverlayIndex] === undefined) { + this.activeOverlayIndex = this.getNextOverlay(true); + } } /** - * Does searching within text array, create a list of search keyword occurences. - * Each keyword "occurency" is saved as index of the the line where keyword founded - * @param text An array of any textual data (logs, for example) + * Does searching within text array, create a list of search keyword occurrences. + * Each keyword "occurrence" is saved as index of the line where keyword was found + * @param lines An array of any textual data (logs, for example) * @param query Search query from input - * @returns {Array} Array of line indexes [0, 0, 14, 17, 17, 17, 20...] + * @returns Array of line indexes [0, 0, 14, 17, 17, 17, 20...] */ - findOccurences(text: string[], query: string) { - if (!text) return []; - const occurences: number[] = []; + private findOccurrences(lines: string[], query?: string): number[] { + const regex = new RegExp(SearchStore.escapeRegex(query), "gi"); - text.forEach((line, index) => { - const regex = new RegExp(this.escapeRegex(query), "gi"); - const matches = [...line.matchAll(regex)]; - - matches.forEach(() => occurences.push(index)); - }); - - return occurences; + return lines + .flatMap((line, index) => Array.from(line.matchAll(regex), () => index)); } /** - * Getting next overlay index within the occurences array + * Getting next overlay index within the occurrences array * @param loopOver Allows to jump from last element to first - * @returns {number} next overlay index + * @returns next overlay index */ - getNextOverlay(loopOver = false) { + private getNextOverlay(loopOver = false): number { const next = this.activeOverlayIndex + 1; if (next > this.occurrences.length - 1) { @@ -72,11 +93,11 @@ export class SearchStore { } /** - * Getting previous overlay index within the occurences array of occurences + * Getting previous overlay index within the occurrences array of occurrences * @param loopOver Allows to jump from first element to last one - * @returns {number} prev overlay index + * @returns previous overlay index */ - getPrevOverlay(loopOver = false) { + private getPrevOverlay(loopOver = false): number { const prev = this.activeOverlayIndex - 1; if (prev < 0) { @@ -87,18 +108,18 @@ export class SearchStore { } @autobind() - setNextOverlayActive() { + public setNextOverlayActive(): void { this.activeOverlayIndex = this.getNextOverlay(true); } @autobind() - setPrevOverlayActive() { + public setPrevOverlayActive(): void { this.activeOverlayIndex = this.getPrevOverlay(true); } /** * Gets line index of where active overlay is located - * @returns {number} A line index within the text/logs array + * @returns A line index within the text/logs array */ @computed get activeOverlayLine(): number { return this.occurrences[this.activeOverlayIndex]; @@ -115,25 +136,17 @@ export class SearchStore { /** * Checks if overlay is active (to highlight it with orange background usually) * @param line Index of the line where overlay is located - * @param occurence Number of the overlay within one line + * @param occurrence Number of the overlay within one line */ @autobind() - isActiveOverlay(line: number, occurence: number) { + public isActiveOverlay(line: number, occurrence: number): boolean { const firstLineIndex = this.occurrences.findIndex(item => item === line); - return firstLineIndex + occurence === this.activeOverlayIndex; - } - - /** - * An utility methods escaping user string to safely pass it into new Regex(variable) - * @param value Unescaped string - */ - escapeRegex(value: string) { - return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ); + return firstLineIndex + occurrence === this.activeOverlayIndex; } @action - reset() { + private reset(): void { this.searchQuery = ""; this.activeOverlayIndex = -1; this.occurrences = []; diff --git a/src/renderer/components/dock/log-list.tsx b/src/renderer/components/dock/log-list.tsx index 3b66f42d86..fc6efacdfa 100644 --- a/src/renderer/components/dock/log-list.tsx +++ b/src/renderer/components/dock/log-list.tsx @@ -8,7 +8,7 @@ import { action, computed, observable } from "mobx"; import { observer } from "mobx-react"; import { Align, ListOnScrollProps } from "react-window"; -import { searchStore } from "../../../common/search-store"; +import { SearchStore, searchStore } from "../../../common/search-store"; import { cssNames } from "../../utils"; import { Button } from "../button"; import { Icon } from "../icon"; @@ -164,7 +164,7 @@ export class LogList extends React.Component { if (searchQuery) { // If search is enabled, replace keyword with backgrounded // Case-insensitive search (lowercasing query and keywords in line) - const regex = new RegExp(searchStore.escapeRegex(searchQuery), "gi"); + const regex = new RegExp(SearchStore.escapeRegex(searchQuery), "gi"); const matches = item.matchAll(regex); const modified = item.replace(regex, match => match.toLowerCase()); // Splitting text line by keyword diff --git a/src/renderer/components/dock/log.store.ts b/src/renderer/components/dock/log.store.ts index 14dc9efdd0..ec80cf4d58 100644 --- a/src/renderer/components/dock/log.store.ts +++ b/src/renderer/components/dock/log.store.ts @@ -59,9 +59,9 @@ export class LogStore { }; /** - * Function is used to refreser/stream-like requests. + * Function is used to refresher/stream-like requests. * It changes 'sinceTime' param each time allowing to fetch logs - * starting from last line recieved. + * starting from last line received. * @param tabId */ loadMore = async (tabId: TabId) => { @@ -91,7 +91,7 @@ export class LogStore { return podsApi.getLogs({ namespace, name }, { ...params, - timestamps: true, // Always setting timestampt to separate old logs from new ones + timestamps: true, // Always setting timestamp to separate old logs from new ones container: selectedContainer.name, previous }).then(result => { @@ -120,11 +120,7 @@ export class LogStore { * Returns logs with timestamps for selected tab */ get logs() { - const id = dockStore.selectedTabId; - - if (!this.podLogs.has(id)) return []; - - return this.podLogs.get(id); + return this.podLogs.get(dockStore.selectedTabId) ?? []; } /**