Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | 2x 2x 2x 2x 2x 27x 27x 27x 27x 27x 10384x 2x 2x 10382x 17x 17x 8x 10365x 109x 10256x 24x 24x 3x 21x 10232x 40x 17x 10192x 10379x 24x 1x 23x 19x 23x 23x 23x 23x 23x 38x 38x 38x 10x 10x 23x 23x 42x 42x 42x 8x 8x 80x 44x 44x 44x 21x 21x 10256x 10380x | import SearchOperator from './SearchOperator'; import { SearchOperators, SearchOperatorType, SearchQuotes, SearchQuoteType, } from './SearchOperatorTypes'; import SearchQuery from './SearchQuery'; import SearchTerm from './SearchTerm'; /** * Class responsible for parsing a search string into a SearchQuery. */ export default class SearchParser { /** * Parses the given search text into a SearchQuery object. * @param searchText - The search text to parse. * @returns A SearchQuery object containing the parsed search elements. */ public static parse(searchText: string): SearchQuery { const query = new SearchQuery(); let openQuote = false; let term = ''; let escapeNextChar = false; // Iterate over each character in the search text for (const char of searchText) { if (char === '\\') { // Escape the next character escapeNextChar = true; continue; } if (!escapeNextChar && this.isQuote(char)) { openQuote = !openQuote; // Add the term if the quote is closed and the term is not empty if (!this.isTermEmpty(term)) { term = this.addTerm(query, term); } } else if (openQuote) { // Add the character to the term if inside a quote term += char; } else if (this.isOperator(char)) { // Add the Operator to the query const previousSearchElement = query.getElements().slice(-1)[0]; // Check for consecutive operators if ( previousSearchElement instanceof SearchOperator && ((previousSearchElement.operator !== '!' && char !== '!') || (previousSearchElement.operator === '!' && char === '!')) ) { throw new Error('Consecutive operators are not allowed'); } this.addOperator(query, char as SearchOperatorType); } else if (char === ' ') { // Spaces outside quotation marks separate terms if (!this.isTermEmpty(term)) { term = this.addTerm(query, term); } } else { term += char; } escapeNextChar = false; } // Check for unmatched quotes if (openQuote) { throw new Error('Unmatched quote'); } // Add the final term to the query if (!this.isTermEmpty(term)) { term = this.addTerm(query, term); } // Add default AND operators between search terms if no operators are specified this.handleDefaultOperator(query); // Combine negations with search terms and remove negation operators this.combineNegationsAndElements(query); return query; } /** * Adds default AND operators between search terms if no operators are specified. * @param query The SearchQuery object to add operators to. */ private static handleDefaultOperator(query: SearchQuery): void { const searchElements = query.getElements(); for (let i = 0; i < searchElements.length - 1; i++) { const currentElement = searchElements[i]; const nextElement = searchElements[i + 1]; if ( currentElement instanceof SearchTerm && nextElement instanceof SearchTerm ) { // Add an AND operator between search terms searchElements.splice(i + 1, 0, new SearchOperator('&')); i++; // Skip the next element to avoid adding multiple operators } } } /** * Combines negations with search terms and removes negation operators. * @param query The SearchQuery object to process. */ private static combineNegationsAndElements(query: SearchQuery): void { const searchElements = query.getElements(); for (let i = 0; i < searchElements.length - 1; i++) { const currentElement = searchElements[i]; const nextElement = searchElements[i + 1]; if ( currentElement instanceof SearchOperator && currentElement.operator === '!' ) { // remove the negation operator searchElements.splice(i, 1); // add the negation to the next element nextElement.isNegated = true; } } } /** * Checks if the term is empty. * @param term The term to check. * @returns True if the term is empty, false otherwise. */ private static isTermEmpty(term: string): boolean { return term.trim() === ''; } /** * Adds a search term to the query. * @param query The SearchQuery object to add the term to. * @param term The term to add. * @returns An empty string. */ private static addTerm(query: SearchQuery, term: string): string { const searchTerm = new SearchTerm(term); query.addElement(searchTerm); return ''; } /** * Adds a search operator to the query. * @param query The SearchQuery object to add the operator to. * @param operator The operator to add. */ private static addOperator( query: SearchQuery, operator: SearchOperatorType, ): void { const searchOperator = new SearchOperator(operator); query.addElement(searchOperator); } /** * Checks if the character is a search operator. * @param char - The character to check. * @returns True if the character is a search operator, false otherwise. */ private static isOperator(char: string): boolean { return SearchOperators.includes(char as SearchOperatorType); } /** * Checks if the character is a quote. * @param char The character to check. * @returns True if the character is a quote, false otherwise. */ private static isQuote(char: string): boolean { return SearchQuotes.includes(char as SearchQuoteType); } } |