11import type { SgSearch } from '../postMessage'
22import { childPort } from '../postMessage'
3- import { useCallback , useMemo , useState , useRef } from 'react'
3+ import { useCallback , useMemo , useSyncExternalStore } from 'react'
44import { useDebounce } from 'react-use'
55
6- type StreamHandler = ( r : SgSearch [ ] ) => void
7-
86// id should not overflow, the MOD is large enough
97// for most cases (unless there is buggy search)
108const MOD = 1e9 + 7
119
1210// maintain the latest search task id and callback
1311let id = 0
14- let currentResolve : ( id : number ) => void
15- let currentReject = ( ) => { }
16- let currentHandler : StreamHandler
12+ let searchResult : SgSearch [ ] = [ ]
13+ let queryInFlight = ''
14+ let searching = true
15+ let notify = ( ) => { }
1716
18- function postSearch ( inputValue : string , onStream : StreamHandler ) {
17+ function postSearch ( inputValue : string ) {
1918 id = ( id + 1 ) % MOD
2019 childPort . postMessage ( 'search' , { id, inputValue } )
21- currentReject ( )
22- currentHandler = onStream
23- return new Promise < number > ( ( resolve , reject ) => {
24- currentResolve = resolve
25- currentReject = reject
26- } )
20+ searchResult = [ ]
21+ searching = true
22+ notify ( )
2723}
2824
2925childPort . onMessage ( 'searchResultStreaming' , event => {
3026 if ( event . id !== id ) {
3127 return
3228 }
33- currentHandler ( event . searchResult )
29+ queryInFlight = event . inputValue
30+ searchResult = searchResult . concat ( event . searchResult )
31+ notify ( )
3432} )
33+
3534childPort . onMessage ( 'searchEnd' , event => {
3635 if ( event . id !== id ) {
3736 return
3837 }
39- currentResolve ( id )
40- currentResolve = ( ) => { }
41- currentReject = ( ) => { }
42- currentHandler = ( ) => { }
38+ searching = false
39+ notify ( )
4340} )
4441
4542function groupBy ( matches : SgSearch [ ] ) {
@@ -53,36 +50,41 @@ function groupBy(matches: SgSearch[]) {
5350 return groups
5451}
5552
53+ // version is for react to update view
54+ let version = 114514
55+ function subscribe ( onChange : ( ) => void ) : ( ) => void {
56+ notify = ( ) => {
57+ onChange ( )
58+ version = ( version + 1 ) % MOD
59+ }
60+ return ( ) => {
61+ // TODO: cleanup is not correct
62+ notify = ( ) => { }
63+ }
64+ }
65+
66+ function getSnapshot ( ) {
67+ return version // symbolic snapshot for react
68+ }
69+
5670export const useSearchResult = ( inputValue : string ) => {
57- const resultRef = useRef < SgSearch [ ] > ( [ ] )
58- const [ searching , setSearching ] = useState ( false )
59- const [ queryInFlight , setQuery ] = useState ( inputValue )
71+ useSyncExternalStore ( subscribe , getSnapshot )
6072
6173 const refreshSearchResult = useCallback ( ( ) => {
62- setSearching ( true )
63- resultRef . current = [ ]
64- postSearch ( inputValue , ret => {
65- resultRef . current . push ( ...ret )
66- setQuery ( inputValue + resultRef . current . length )
67- } )
68- . then ( ( ) => {
69- setSearching ( false )
70- } )
71- . catch ( ( ) => {
72- // TODO: cancelled request, should send cancel to extension
73- } )
74+ // TODO: cancelled request, should send cancel to extension
75+ postSearch ( inputValue )
7476 } , [ inputValue ] )
7577
7678 const groupedByFileSearchResult = useMemo ( ( ) => {
77- return [ ...groupBy ( resultRef . current ) . entries ( ) ]
78- } , [ resultRef . current . length ] )
79+ return [ ...groupBy ( searchResult ) . entries ( ) ]
80+ } , [ searchResult ] )
7981
8082 useDebounce ( refreshSearchResult , 100 , [ inputValue ] )
8183
8284 return {
8385 queryInFlight,
8486 searching,
85- searchResult : resultRef . current ,
87+ searchResult,
8688 groupedByFileSearchResult,
8789 refreshSearchResult
8890 }
0 commit comments