@@ -13,6 +13,7 @@ export interface CommandPaletteItem {
1313 readonly kind : "action" | "submenu" ;
1414 readonly value : string ;
1515 readonly searchTerms : ReadonlyArray < string > ;
16+ readonly searchIndex ?: CommandPaletteSearchIndex ;
1617 readonly title : ReactNode ;
1718 readonly description ?: string ;
1819 readonly timestamp ?: string ;
@@ -24,6 +25,11 @@ export interface CommandPaletteItem {
2425 readonly shortcutCommand ?: KeybindingCommand ;
2526}
2627
28+ export interface CommandPaletteSearchIndex {
29+ readonly normalizedTerms : ReadonlyArray < string > ;
30+ readonly haystack : string ;
31+ }
32+
2733export interface CommandPaletteActionItem extends CommandPaletteItem {
2834 readonly kind : "action" ;
2935 readonly keepOpen ?: boolean ;
@@ -87,23 +93,39 @@ export function normalizeSearchText(value: string): string {
8793 return value . trim ( ) . toLowerCase ( ) . replace ( / \s + / g, " " ) ;
8894}
8995
96+ export function buildCommandPaletteSearchIndex (
97+ searchTerms : ReadonlyArray < string > ,
98+ ) : CommandPaletteSearchIndex {
99+ return {
100+ normalizedTerms : searchTerms
101+ . filter ( ( term ) => term . length > 0 )
102+ . map ( ( term ) => normalizeSearchText ( term ) ) ,
103+ haystack : normalizeSearchText ( searchTerms . join ( " " ) ) ,
104+ } ;
105+ }
106+
90107export function buildProjectActionItems ( input : {
91108 projects : ReadonlyArray < Project > ;
92109 valuePrefix : string ;
93110 icon : ( project : Project ) => ReactNode ;
94111 runProject : ( project : Project ) => Promise < void > ;
95112} ) : CommandPaletteActionItem [ ] {
96- return input . projects . map ( ( project ) => ( {
97- kind : "action" ,
98- value : `${ input . valuePrefix } :${ project . environmentId } :${ project . id } ` ,
99- searchTerms : [ project . name , project . cwd ] ,
100- title : project . name ,
101- description : project . cwd ,
102- icon : input . icon ( project ) ,
103- run : async ( ) => {
104- await input . runProject ( project ) ;
105- } ,
106- } ) ) ;
113+ return input . projects . map ( ( project ) => {
114+ const searchTerms = [ project . name , project . cwd ] ;
115+
116+ return {
117+ kind : "action" ,
118+ value : `${ input . valuePrefix } :${ project . environmentId } :${ project . id } ` ,
119+ searchTerms,
120+ searchIndex : buildCommandPaletteSearchIndex ( searchTerms ) ,
121+ title : project . name ,
122+ description : project . cwd ,
123+ icon : input . icon ( project ) ,
124+ run : async ( ) => {
125+ await input . runProject ( project ) ;
126+ } ,
127+ } ;
128+ } ) ;
107129}
108130
109131export type BuildThreadActionItemsThread = Pick <
@@ -150,12 +172,14 @@ export function buildThreadActionItems<TThread extends BuildThreadActionItemsThr
150172
151173 const leadingContent = input . renderLeadingContent ?.( thread ) ;
152174 const trailingContent = input . renderTrailingContent ?.( thread ) ;
175+ const searchTerms = [ thread . title , projectTitle ?? `` , thread . branch ?? `` ] ;
153176
154177 return Object . assign (
155178 {
156179 kind : "action" as const ,
157180 value : `thread:${ thread . id } ` ,
158- searchTerms : [ thread . title , projectTitle ?? `` , thread . branch ?? `` ] ,
181+ searchTerms,
182+ searchIndex : buildCommandPaletteSearchIndex ( searchTerms ) ,
159183 title : thread . title ,
160184 description : descriptionParts . join ( ` · ` ) ,
161185 timestamp : formatRelativeTimeLabel (
@@ -174,8 +198,7 @@ export function buildThreadActionItems<TThread extends BuildThreadActionItemsThr
174198 } ) ;
175199}
176200
177- function rankSearchFieldMatch ( field : string , normalizedQuery : string ) : number {
178- const normalizedField = normalizeSearchText ( field ) ;
201+ function rankNormalizedSearchFieldMatch ( normalizedField : string , normalizedQuery : string ) : number {
179202 if ( normalizedField . length === 0 || ! normalizedField . includes ( normalizedQuery ) ) {
180203 return Number . NEGATIVE_INFINITY ;
181204 }
@@ -188,19 +211,24 @@ function rankSearchFieldMatch(field: string, normalizedQuery: string): number {
188211 return 1 ;
189212}
190213
191- function rankCommandPaletteItemMatch (
214+ function getCommandPaletteSearchIndex (
192215 item : CommandPaletteActionItem | CommandPaletteSubmenuItem ,
216+ ) : CommandPaletteSearchIndex {
217+ return item . searchIndex ?? buildCommandPaletteSearchIndex ( item . searchTerms ) ;
218+ }
219+
220+ function rankCommandPaletteSearchIndexMatch (
221+ index : CommandPaletteSearchIndex ,
193222 normalizedQuery : string ,
194223) : number {
195- const terms = item . searchTerms . filter ( ( term ) => term . length > 0 ) ;
196- if ( terms . length === 0 ) {
224+ if ( index . normalizedTerms . length === 0 ) {
197225 return 0 ;
198226 }
199227
200- for ( const [ index , field ] of terms . entries ( ) ) {
201- const fieldRank = rankSearchFieldMatch ( field , normalizedQuery ) ;
228+ for ( const [ termIndex , normalizedField ] of index . normalizedTerms . entries ( ) ) {
229+ const fieldRank = rankNormalizedSearchFieldMatch ( normalizedField , normalizedQuery ) ;
202230 if ( fieldRank !== Number . NEGATIVE_INFINITY ) {
203- return 1_000 - index * 100 + fieldRank ;
231+ return 1_000 - termIndex * 100 + fieldRank ;
204232 }
205233 }
206234
@@ -253,15 +281,15 @@ export function filterCommandPaletteGroups(input: {
253281 return searchableGroups . flatMap ( ( group ) => {
254282 const items = group . items
255283 . map ( ( item , index ) => {
256- const haystack = normalizeSearchText ( item . searchTerms . join ( " " ) ) ;
257- if ( ! haystack . includes ( normalizedQuery ) ) {
284+ const searchIndex = getCommandPaletteSearchIndex ( item ) ;
285+ if ( ! searchIndex . haystack . includes ( normalizedQuery ) ) {
258286 return null ;
259287 }
260288
261289 return {
262290 item,
263291 index,
264- rank : rankCommandPaletteItemMatch ( item , normalizedQuery ) ,
292+ rank : rankCommandPaletteSearchIndexMatch ( searchIndex , normalizedQuery ) ,
265293 } ;
266294 } )
267295 . filter (
0 commit comments