Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/components/Search/SearchRouter/SearchRouterUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import type {SearchQueryItem} from '@components/Search/SearchList/ListItem/Searc

import {getPolicyNameWithFallback, sanitizeSearchValue} from '@libs/SearchQueryUtils';

import type {ReportsSplitNavigatorParamList} from '@navigation/types';

import CONST from '@src/CONST';
import SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
Expand Down Expand Up @@ -50,8 +48,9 @@ function getContextualReportData(state: NavigationState | undefined): Contextual
}

if (maybeReportRoute?.name === SCREENS.REPORT || maybeReportRoute?.name === SCREENS.RIGHT_MODAL.EXPENSE_REPORT) {
// We're guaranteed that the type of params is of SCREENS.REPORT
return {contextualReportID: (maybeReportRoute?.params as ReportsSplitNavigatorParamList[typeof SCREENS.REPORT]).reportID, isSearchRouterScreen};
const params = maybeReportRoute.params;
const reportID = params && 'reportID' in params ? params.reportID : undefined;
return {contextualReportID: typeof reportID === 'string' ? reportID : undefined, isSearchRouterScreen};
}
return {contextualReportID: undefined, isSearchRouterScreen};
}
Expand Down
7 changes: 3 additions & 4 deletions src/components/Search/SearchRouter/buildSubstitutionsMap.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type {LocaleContextProps, LocalizedTranslate} from '@components/LocaleContextProvider';
import type {SearchAutocompleteQueryRange} from '@components/Search/types';

import {parse} from '@libs/SearchParser/autocompleteParser';
import {getFilterDisplayValue} from '@libs/SearchQueryUtils';
Expand Down Expand Up @@ -45,7 +44,7 @@ function buildSubstitutionsMap(
reportAttributes: ReportAttributesDerivedValue['reports'] | undefined,
bankAccountList?: BankAccountList,
): SubstitutionMap {
const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]};
const parsedQuery = parse(query);

const searchAutocompleteQueryRanges = parsedQuery.ranges;
if (searchAutocompleteQueryRanges.length === 0) {
Expand All @@ -54,7 +53,7 @@ function buildSubstitutionsMap(

const substitutionKeyOccurrences = new Map<string, number>();

const substitutionsMap = searchAutocompleteQueryRanges.reduce((map, range) => {
const substitutionsMap = searchAutocompleteQueryRanges.reduce<SubstitutionMap>((map, range) => {
const {key: filterKey, value: filterValue} = range;

if (filterKey === CONST.SEARCH.SYNTAX_FILTER_KEYS.TAX_RATE) {
Expand Down Expand Up @@ -116,7 +115,7 @@ function buildSubstitutionsMap(
}

return map;
}, {} as SubstitutionMap);
}, {});
return substitutionsMap;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type {SearchAutocompleteResult} from '@components/Search/types';

import {parse as parseSearchQuery} from '@libs/SearchParser/autocompleteParser';

function getAutocompleteSelectionSubstitutionKey(newSearchQuery: string, fieldKey: string, fallbackMapKey: string, fallbackSearchQuery: string): string {
const parsed = parseSearchQuery(newSearchQuery) as SearchAutocompleteResult;
const parsed = parseSearchQuery(newSearchQuery);
const sameKeyRanges = parsed.ranges?.filter((range) => range.key === fieldKey) ?? [];
const lastRange = sameKeyRanges.at(-1);
const rangeValue = lastRange?.value ?? fallbackSearchQuery;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {SearchAutocompleteQueryRange, SearchFilterKey} from '@components/Search/types';
import type {SearchAutocompleteParserRange} from '@components/Search/types';

import {parse} from '@libs/SearchParser/autocompleteParser';
import {sanitizeSearchValue} from '@libs/SearchQueryUtils';
Expand All @@ -7,7 +7,7 @@ import CONST from '@src/CONST';

type SubstitutionMap = Record<string, string>;

const getSubstitutionMapKey = (filterKey: SearchFilterKey, value: string) => `${filterKey}:${value}`;
const getSubstitutionMapKey = (filterKey: SearchAutocompleteParserRange['key'], value: string) => `${filterKey}:${value}`;

const USER_FILTER_KEYS = new Set<string>([
CONST.SEARCH.SYNTAX_FILTER_KEYS.FROM,
Expand All @@ -23,7 +23,7 @@ const USER_FILTER_KEYS = new Set<string>([
* Key for the Nth occurrence of the same filter+value (e.g. multiple workspaces with the same name).
* Index 0 uses the base key for backward compatibility; index > 0 uses baseKey:index.
*/
const getSubstitutionMapKeyWithIndex = (filterKey: SearchFilterKey, value: string, index: number) =>
const getSubstitutionMapKeyWithIndex = (filterKey: SearchAutocompleteParserRange['key'], value: string, index: number) =>
index === 0 ? getSubstitutionMapKey(filterKey, value) : `${getSubstitutionMapKey(filterKey, value)}:${index}`;

/**
Expand All @@ -41,7 +41,7 @@ const getSubstitutionMapKeyWithIndex = (filterKey: SearchFilterKey, value: strin
* return: `A from:9876 A`
*/
function getQueryWithSubstitutions(changedQuery: string, substitutions: SubstitutionMap, currentUserAccountID?: number) {
const parsed = parse(changedQuery) as {ranges: SearchAutocompleteQueryRange[]};
const parsed = parse(changedQuery);

const searchAutocompleteQueryRanges = parsed.ranges;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type {SearchAutocompleteQueryRange} from '@components/Search/types';

import {parse} from '@libs/SearchParser/autocompleteParser';

import type {SubstitutionMap} from './getQueryWithSubstitutions';
Expand All @@ -20,7 +18,7 @@ import {getSubstitutionMapKeyWithIndex} from './getQueryWithSubstitutions';
* return: {}
*/
function getUpdatedSubstitutionsMap(query: string, substitutions: SubstitutionMap): SubstitutionMap {
const parsedQuery = parse(query) as {ranges: SearchAutocompleteQueryRange[]};
const parsedQuery = parse(query);

const searchAutocompleteQueryRanges = parsedQuery.ranges;

Expand Down
14 changes: 14 additions & 0 deletions src/components/Search/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import type {UnitPosition, UnitWithFallback} from '@components/Charts';
import type {PaymentMethod} from '@components/KYCWall/types';
import type {SelectionListStyle} from '@components/SelectionList/types';
Expand Down Expand Up @@ -411,6 +411,18 @@
value: string;
};

/** Parser keys retain input spelling and can include dynamic report field suffixes. */
type SearchAutocompleteParserRange = Omit<SearchAutocompleteQueryRange, 'key'> & {
key: string;
negated: boolean;
};

type SearchAutocompleteParserResult = {
/** Whitespace trimmed from an identifier can leave only its key and negation. */
autocomplete: SearchAutocompleteParserRange | Pick<SearchAutocompleteParserRange, 'key' | 'negated'> | null;
ranges: SearchAutocompleteParserRange[];
};

type SearchParams = {
queryJSON: Readonly<SearchQueryJSON>;
searchKey: SearchKey | undefined;
Expand Down Expand Up @@ -478,6 +490,8 @@
};

export type {
SearchAutocompleteParserRange,
SearchAutocompleteParserResult,
SelectedTransactionInfo,
SelectedTransactions,
SearchColumnType,
Expand Down
67 changes: 67 additions & 0 deletions src/libs/SearchParser/autocompleteParser.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Describes autocompleteParser.peggy and baseRules.peggy after parser-workletization.sh.
* Keep this contract aligned with the shipped autocompleteParser.js when regenerating it.
*/
import type {SearchAutocompleteParserResult} from '@components/Search/types';

type ParserExpectation =
| {type: 'literal'; text: string; ignoreCase: boolean}
| {type: 'class'; parts: Array<string | [string, string]>; inverted: boolean; ignoreCase: boolean}
| {type: 'any'}
| {type: 'end'}
| {type: 'other'; description: string};

type ParserPosition = {
offset: number;
line: number;
column: number;
};

type ParserLocation = {
source: unknown;
start: ParserPosition;
end: ParserPosition;
};

type ParserOptions = {
startRule?: 'query' | '';
grammarSource?: unknown;
peg$currPos?: number;
peg$silentFails?: number;
peg$maxFailExpected?: ParserExpectation[];
peg$library?: boolean;
};

type ParserLibraryResult = {
peg$result: SearchAutocompleteParserResult;
peg$currPos: number;
peg$FAILED: Record<string, never>;
peg$maxFailExpected: ParserExpectation[];
peg$maxFailPos: number;
};

declare function parse(input: string, options?: ParserOptions & {peg$library?: false}): SearchAutocompleteParserResult;
declare function parse(input: string, options: ParserOptions & {peg$library: true}): ParserLibraryResult;
declare function parse(input: string, options?: ParserOptions): SearchAutocompleteParserResult | ParserLibraryResult;

/** Initially contains query. Mutating this list does not register additional parser rules. */
declare const StartRules: string[];

/** Workletization replaces the Error subclass with an empty constructor that ignores its arguments. */
declare class SyntaxError {
constructor(message?: string, expected?: ParserExpectation[], found?: string | null, location?: ParserLocation);

message?: string;

expected?: ParserExpectation[];

found?: string | null;

location?: ParserLocation;

format(sources: Array<{source: unknown; text: string}>): string;

static buildMessage(expected: ParserExpectation[], found: string | null): string;
}

export {parse, StartRules, SyntaxError};
Loading