@@ -22,9 +22,31 @@ function asyncBufferFactory({ url, byteLength }: { url: string; byteLength?: num
2222 return cached
2323}
2424
25+ const exampleQueries = [ 'certain' , 'javascript' , 'python' , 'import (numpy|pandas)' ]
26+
27+ // A grep query with regex metacharacters that compiles is run as a RegExp;
28+ // anything else is a plain substring.
29+ function parseGrepQuery ( input : string ) : string | RegExp {
30+ if ( ! / [ [ \] ( ) { } . * + ? ^ $ | \\ ] / . test ( input ) ) return input
31+ try {
32+ return new RegExp ( input )
33+ } catch {
34+ return input
35+ }
36+ }
37+
38+ function grepMatch ( value : string , rawQuery : string ) : { index : number ; length : number } | undefined {
39+ const query = parseGrepQuery ( rawQuery )
40+ if ( query instanceof RegExp ) {
41+ const match = new RegExp ( query . source , query . flags ) . exec ( value )
42+ return match ? { index : match . index , length : match [ 0 ] . length } : undefined
43+ }
44+ const index = value . toLowerCase ( ) . indexOf ( rawQuery . toLowerCase ( ) )
45+ return index < 0 ? undefined : { index, length : rawQuery . length }
46+ }
47+
2548/**
2649 * Hypgrep demo page
27- * Try "Vongphachanh" or "gratianopolitanus" as a search term
2850 *
2951 * @param {Object } props
3052 * @returns {ReactNode }
@@ -61,7 +83,7 @@ export default function Page({ df, name, byteLength, setError }: PageProps): Rea
6183 const url = name
6284 const rowGen = parquetFind ( {
6385 url,
64- query,
86+ query : parseGrepQuery ( query ) ,
6587 limit : 20 ,
6688 asyncBufferFactory,
6789 indexMetadata,
@@ -101,31 +123,18 @@ export default function Page({ df, name, byteLength, setError }: PageProps): Rea
101123 } , [ name , setError ] )
102124
103125 const renderCellContent = useCallback ( ( { cell, stringify } : CellContentProps ) => {
104- // Find first keyword match and highlight it
105- const queryKeys = query . split ( ' ' ) . map ( q => q . toLowerCase ( ) )
106126 const value : unknown = cell ?. value
107- if ( typeof value === 'string' && queryKeys . length > 0 ) {
108- const lowerValue = value . toLowerCase ( )
109- let firstIndex = - 1
110- let firstLength = 0
111- for ( const q of queryKeys ) {
112- const index = lowerValue . indexOf ( q )
113- if ( index >= 0 && ( firstIndex === - 1 || index < firstIndex ) ) {
114- firstIndex = index
115- firstLength = q . length
116- }
117- }
118- if ( firstIndex >= 0 ) {
119- const truncateBefore = firstIndex > 20 ? '...' : ''
120- return < >
121- { truncateBefore }
122- { value . slice ( 0 , firstIndex ) . slice ( - 20 ) }
123- < mark > { value . slice ( firstIndex , firstIndex + firstLength ) } </ mark >
124- { value . slice ( firstIndex + firstLength ) }
125- </ >
126- }
127- }
128- return stringify ( value )
127+ if ( typeof value !== 'string' ) return stringify ( value )
128+ const match = grepMatch ( value , query )
129+ if ( ! match ) return stringify ( value )
130+ const { index, length } = match
131+ const truncateBefore = index > 20 ? '...' : ''
132+ return < >
133+ { truncateBefore }
134+ { value . slice ( 0 , index ) . slice ( - 20 ) }
135+ < mark > { value . slice ( index , index + length ) } </ mark >
136+ { value . slice ( index + length ) }
137+ </ >
129138 } , [ query ] )
130139
131140 return < >
@@ -147,6 +156,14 @@ export default function Page({ df, name, byteLength, setError }: PageProps): Rea
147156 />
148157 </ div >
149158 </ div >
159+ { ! query && < div className = 'examples-row' >
160+ < span className = 'examples-label' > Try:</ span >
161+ < div className = 'examples' >
162+ { exampleQueries . map ( example =>
163+ < button key = { example } className = 'example-chip' onClick = { ( ) => { setQuery ( example ) } } > { example } </ button > ,
164+ ) }
165+ </ div >
166+ </ div > }
150167 < HighTable
151168 focus = { false }
152169 cacheKey = { name }
0 commit comments