|
3 | 3 | import { RepositoryInfo, SearchRequest, SearchResponse, SearchResultFile } from '@/features/search/types'; |
4 | 4 | import { useState, useCallback, useRef, useEffect } from 'react'; |
5 | 5 |
|
| 6 | +interface CacheEntry { |
| 7 | + files: SearchResultFile[]; |
| 8 | + repoInfo: Record<number, RepositoryInfo>; |
| 9 | + numMatches: number; |
| 10 | + durationMs: number; |
| 11 | + timestamp: number; |
| 12 | +} |
| 13 | + |
| 14 | +const searchCache = new Map<string, CacheEntry>(); |
| 15 | +const CACHE_TTL = 5 * 60 * 1000; |
| 16 | + |
| 17 | +const createCacheKey = (params: SearchRequest): string => { |
| 18 | + return JSON.stringify({ |
| 19 | + query: params.query, |
| 20 | + matches: params.matches, |
| 21 | + contextLines: params.contextLines, |
| 22 | + whole: params.whole, |
| 23 | + isRegexEnabled: params.isRegexEnabled, |
| 24 | + isCaseSensitivityEnabled: params.isCaseSensitivityEnabled, |
| 25 | + }); |
| 26 | +}; |
| 27 | + |
| 28 | +const isCacheValid = (entry: CacheEntry): boolean => { |
| 29 | + return Date.now() - entry.timestamp < CACHE_TTL; |
| 30 | +}; |
6 | 31 |
|
7 | 32 | export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegexEnabled, isCaseSensitivityEnabled }: SearchRequest) => { |
8 | 33 |
|
@@ -44,6 +69,30 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex |
44 | 69 | } |
45 | 70 | abortControllerRef.current = new AbortController(); |
46 | 71 |
|
| 72 | + const cacheKey = createCacheKey({ |
| 73 | + query, |
| 74 | + matches, |
| 75 | + contextLines, |
| 76 | + whole, |
| 77 | + isRegexEnabled, |
| 78 | + isCaseSensitivityEnabled, |
| 79 | + }); |
| 80 | + |
| 81 | + // Check if we have a valid cached result. If so, use it. |
| 82 | + const cachedEntry = searchCache.get(cacheKey); |
| 83 | + if (cachedEntry && isCacheValid(cachedEntry)) { |
| 84 | + console.debug('Using cached search results'); |
| 85 | + setState({ |
| 86 | + isStreaming: false, |
| 87 | + error: null, |
| 88 | + files: cachedEntry.files, |
| 89 | + repoInfo: cachedEntry.repoInfo, |
| 90 | + durationMs: cachedEntry.durationMs, |
| 91 | + numMatches: cachedEntry.numMatches, |
| 92 | + }); |
| 93 | + return; |
| 94 | + } |
| 95 | + |
47 | 96 | setState({ |
48 | 97 | isStreaming: true, |
49 | 98 | error: null, |
@@ -153,10 +202,19 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex |
153 | 202 | } finally { |
154 | 203 | const endTime = performance.now(); |
155 | 204 | const durationMs = endTime - startTime; |
156 | | - setState(prev => ({ |
157 | | - ...prev, |
158 | | - durationMs, |
159 | | - })); |
| 205 | + setState(prev => { |
| 206 | + searchCache.set(cacheKey, { |
| 207 | + files: prev.files, |
| 208 | + repoInfo: prev.repoInfo, |
| 209 | + numMatches: prev.numMatches, |
| 210 | + durationMs, |
| 211 | + timestamp: Date.now(), |
| 212 | + }); |
| 213 | + return { |
| 214 | + ...prev, |
| 215 | + durationMs, |
| 216 | + } |
| 217 | + }); |
160 | 218 | } |
161 | 219 | } |
162 | 220 |
|
|
0 commit comments