import { equalMatch , exactMatchNaive , fuzzyMatchV2 , prefixMatch , suffixMatch , } from "./algo" ; import { normalizeRune } from "./normalize" ; import { Rune , runesToStr , strToRunes } from "./runes" ; import { Casing } from "./types" ; export enum TermType { Fuzzy , Exact , Prefix , Suffix , Equal } export const termTypeMap = { [ TermType . Fuzzy ] : fuzzyMatchV2 , [ TermType . Exact ] : exactMatchNaive , [ TermType . Prefix ] : prefixMatch , [ TermType . Suffix ] : suffixMatch , [ TermType . Equal ] : equalMatch , } ; interface Term { typ : TermType ; inv : boolean ; text : Rune [ ] ; caseSensitive : boolean ; normalize : boolean ; } type TermSet = Term [ ] ; export function buildPatternForExtendedMatch ( fuzzy , caseMode , normalize , str ) { let cacheable = true ; str = str . trimLeft ( ) ; { const trimmedAtRightStr = str . trimRight ( ) ; if ( trimmedAtRightStr . endsWith ( "\\" ) && str [ trimmedAtRightStr . length ] === " " ) { str = trimmedAtRightStr + " " ; } else { str = trimmedAtRightStr ; } } let sortable = false ; let termSets = [ ] ; termSets = parseTerms ( fuzzy , caseMode , normalize , str ) ; Loop : for ( const termSet of termSets ) { for ( const [ idx , term ] of termSet . entries ( ) ) { if ( ! term . inv ) { sortable = true ; } if ( ! cacheable || idx > 0 || term . inv || ( fuzzy && term . typ !== TermType . Fuzzy ) || ( ! fuzzy && term . typ !== TermType . Exact ) ) { cacheable = false ; if ( sortable ) { break Loop ; } } } } return { str , termSets , sortable , cacheable , fuzzy , } ; } function parseTerms ( fuzzy , caseMode , normalize , str ) { str = str . replace ( /\\ /g , "\t" ) ; const tokens = str . split ( / +/ ) ; const sets = [ ] ; let set = [ ] ; let switchSet = false ; let afterBar = false ; for ( const token of tokens ) { let typ = TermType . Fuzzy , inv = false , text = token . replace ( /\t/g , " " ) ; const lowerText = text . toLowerCase ( ) ; const caseSensitive = caseMode === "case-sensitive" || ( caseMode === "smart-case" && text !== lowerText ) ; const normalizeTerm = normalize && lowerText === runesToStr ( strToRunes ( lowerText ) . map ( normalizeRune ) ) ; if ( ! caseSensitive ) { text = lowerText ; } if ( ! fuzzy ) { typ = TermType . Exact ; } if ( set . length > 0 && ! afterBar && text === "|" ) { switchSet = false ; afterBar = true ; continue ; } afterBar = false ; if ( text . startsWith ( "!" ) ) { inv = true ; typ = TermType . Exact ; text = text . substring ( 1 ) ; } if ( text !== "$" && text . endsWith ( "$" ) ) { typ = TermType . Suffix ; text = text . substring ( 0 , text . length - 1 ) ; } if ( text . startsWith ( "'" ) ) { if ( fuzzy && ! inv ) { typ = TermType . Exact ; } else { typ = TermType . Fuzzy ; } text = text . substring ( 1 ) ; } else if ( text . startsWith ( "^" ) ) { if ( typ === TermType . Suffix ) { typ = TermType . Equal ; } else { typ = TermType . Prefix ; } text = text . substring ( 1 ) ; } if ( text . length > 0 ) { if ( switchSet ) { sets . push ( set ) ; set = [ ] ; } let textRunes = strToRunes ( text ) ; if ( normalizeTerm ) { textRunes = textRunes . map ( normalizeRune ) ; } set . push ( { typ , inv , text : textRunes , caseSensitive , normalize : normalizeTerm , } ) ; switchSet = true ; } } if ( set . length > 0 ) { sets . push ( set ) ; } return sets ; } export const buildPatternForBasicMatch = ( query , casing , normalize ) => { let caseSensitive = false ; switch ( casing ) { case "smart-case" : if ( query . toLowerCase ( ) !== query ) { caseSensitive = true ; } break ; case "case-sensitive" : caseSensitive = true ; break ; case "case-insensitive" : query = query . toLowerCase ( ) ; caseSensitive = false ; break ; } let queryRunes = strToRunes ( query ) ; if ( normalize ) { queryRunes = queryRunes . map ( normalizeRune ) ; } return { queryRunes , caseSensitive , } ; } ;