Skip to content

Commit

Permalink
search: reduce search flickering
Browse files Browse the repository at this point in the history
  • Loading branch information
bobheadxi committed Dec 16, 2024
1 parent 6f1b19b commit cd783be
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/components/SearchCommand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export default function SearchCommand({ src, props }: { src: Sourcegraph; props?
)}

{/* results */}
<List.Section title="Results" subtitle={searchSummary}>
<List.Section title="Results" subtitle={searchSummary || (state.isLoading ? "Searching..." : undefined)}>
{state.results.map((searchResult, i) => (
<SearchResultItem
key={`result-item-${i}`}
Expand Down
76 changes: 44 additions & 32 deletions src/hooks/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export interface SearchState {
* Set of all results received, up to the provided maximum
*/
results: SearchResult[];
/**
* Whether the results are from a previous search
*/
isPreviousResults: boolean;
/**
* Suggestions that should be rendered for the user if no results are found
*/
Expand All @@ -29,10 +33,6 @@ export interface SearchState {
* Whether a search is currently being executed
*/
isLoading: boolean;
/**
* The previously executed search
*/
previousSearch?: string;
/**
* The previously used pattern type
*/
Expand All @@ -41,11 +41,11 @@ export interface SearchState {

const emptyState: SearchState = {
results: [],
isPreviousResults: false,
suggestions: [],
summary: "",
summaryDetail: undefined,
isLoading: false,
previousSearch: undefined,
previousPatternType: undefined,
};

Expand All @@ -57,6 +57,7 @@ const emptyState: SearchState = {
*/
export function useSearch(src: Sourcegraph, maxResults: number) {
const [state, setState] = useState<SearchState>(emptyState);
const lastSearchRef = useRef<{ search: string; pattern: PatternType }>();
const cancelRef = useRef<AbortController | null>(null);
const { push } = useNavigation();

Expand All @@ -69,7 +70,10 @@ export function useSearch(src: Sourcegraph, maxResults: number) {
}

// Do not repeat searches that are essentially the same
if ((state.previousSearch || "").trim() === searchText.trim() && state.previousPatternType === pattern) {
if (
(lastSearchRef.current?.search || "").trim() === searchText.trim() &&
lastSearchRef.current?.pattern === pattern
) {
return;
}

Expand All @@ -78,44 +82,45 @@ export function useSearch(src: Sourcegraph, maxResults: number) {
cancelRef.current = new AbortController();

// Reset state for new search
setState({
...emptyState,
isLoading: true,
previousSearch: searchText,
setState((oldState) => {
if (lastSearchRef.current?.search && searchText.startsWith(lastSearchRef.current?.search)) {
return {
...emptyState,
isLoading: true,
// Preserve the previous results to avoid flickering
results: oldState.results,
isPreviousResults: true,
};
}
return {
...emptyState,
isLoading: true,
};
});

// Search is starting - track current search as the last search.
lastSearchRef.current = { search: searchText, pattern };

try {
// Update search history
await SearchHistory.addSearch(src, searchText);

const noResultsTimeoutSeconds = 10;
setTimeout(() => {
if (state.results.length === 0) {
setState((oldState) => {
// Cancel search if there are no results yet after a timeout
if (oldState.results.length === 0) {
cancelRef.current?.abort();
return {
...oldState,
isLoading: false,
summary: `No results found in ${noResultsTimeoutSeconds} seconds`,
};
}
return oldState;
});
}
}, noResultsTimeoutSeconds * 1000);

// Do the search
await performSearch(cancelRef.current, src, searchText, pattern, {
onResults: (results) => {
setState((oldState) => {
// We're getting new results, so reset previous results
if (oldState.isPreviousResults) {
oldState.results = [];
}
// We should not render any more results.
if (oldState.results.length > maxResults) {
return oldState; // do nothing
}
const newResults = oldState.results.concat(results);
return {
...oldState,
isPreviousResults: false,
results: newResults,
summaryDetail: `${newResults.length} results shown`,
};
Expand Down Expand Up @@ -147,10 +152,17 @@ export function useSearch(src: Sourcegraph, maxResults: number) {
const results =
matchCount === 0 ? `${matchCount}` : `${Intl.NumberFormat().format(matchCount)}${skipped ? "+" : ""}`;

setState((oldState) => ({
...oldState,
summary: `Found ${results} results in ${duration}`,
}));
setState((oldState) => {
// If it's been a second or so and there's been no results, clear
// the previous results as it's likely they are no longer relevant.
if (oldState.isPreviousResults && durationMs > 2000) {
oldState.results = [];
}
return {
...oldState,
summary: `Found ${results} results in ${duration}`,
};
});
},
});
} catch (error) {
Expand Down

0 comments on commit cd783be

Please sign in to comment.