diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 3b8ebb0eaa2..f5174129360 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -34,7 +34,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { TreeResourceNavigator, WorkbenchObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IProgressService, IProgressStep, IProgress } from 'vs/platform/progress/common/progress'; -import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, VIEW_ID, SearchSortOrder } from 'vs/workbench/services/search/common/search'; +import { IPatternInfo, ISearchComplete, ISearchConfiguration, ISearchConfigurationProperties, ITextQuery, VIEW_ID, SearchSortOrder, SearchCompletionExitCode } from 'vs/workbench/services/search/common/search'; import { ISearchHistoryService, ISearchHistoryValues } from 'vs/workbench/contrib/search/common/searchHistoryService'; import { diffInserted, diffInsertedOutline, diffRemoved, diffRemovedOutline, editorFindMatchHighlight, editorFindMatchHighlightBorder, listActiveSelectionForeground, foreground } from 'vs/platform/theme/common/colorRegistry'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -1348,7 +1348,7 @@ export class SearchView extends ViewPane { this.inputPatternIncludes.onSearchSubmit(); }); - this.viewModel.cancelSearch(); + this.viewModel.cancelSearch(true); this.currentSearchQ = this.currentSearchQ .then(() => this.doSearch(query, excludePatternText, includePatternText, triggeredOnType)) @@ -1393,6 +1393,10 @@ export class SearchView extends ViewPane { this.updateActions(); const hasResults = !this.viewModel.searchResult.isEmpty(); + if (completed?.exit === SearchCompletionExitCode.NewSearchStarted) { + return; + } + if (completed && completed.limitHit) { this.searchWidget.searchInput.showMessage({ content: nls.localize('searchMaxResultsWarning', "The result set only contains a subset of all matches. Please be more specific in your search to narrow down the results."), diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 863e6cbe446..428becff1e7 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -154,7 +154,6 @@ export class SearchWidget extends Widget { private readonly _onDidToggleContext = new Emitter(); readonly onDidToggleContext: Event = this._onDidToggleContext.event; - private temporarilySkipSearchOnChange = false; private showContextCheckbox!: Checkbox; private contextLinesInput!: InputBox; @@ -488,12 +487,10 @@ export class SearchWidget extends Widget { this.setReplaceAllActionState(false); if (this.searchConfiguration.searchOnType) { - if (!this.temporarilySkipSearchOnChange) { - this._onSearchCancel.fire({ focus: false }); - if (this.searchInput.getRegex()) { - try { - const regex = new RegExp(this.searchInput.getValue(), 'ug'); - const matchienessHeuristic = ` + if (this.searchInput.getRegex()) { + try { + const regex = new RegExp(this.searchInput.getValue(), 'ug'); + const matchienessHeuristic = ` ~!@#$%^&*()_+ \`1234567890-= qwertyuiop[]\\ @@ -503,18 +500,17 @@ export class SearchWidget extends Widget { zxcvbnm,./ ZXCVBNM<>? `.match(regex)?.length ?? 0; - const delayMultiplier = - matchienessHeuristic < 50 ? 1 : - matchienessHeuristic < 100 ? 5 : // expressions like `.` or `\w` - 10; // only things matching empty string + const delayMultiplier = + matchienessHeuristic < 50 ? 1 : + matchienessHeuristic < 100 ? 5 : // expressions like `.` or `\w` + 10; // only things matching empty string - this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier); - } catch { - // pass - } - } else { - this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod); + this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier); + } catch { + // pass } + } else { + this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod); } } } diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index b0c40aa7ca6..bea090b9701 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -20,7 +20,7 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; -import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchConfigurationProperties, ISearchService, ITextQuery, ITextSearchPreviewOptions, ITextSearchMatch, ITextSearchStats, resultIsMatch, ISearchRange, OneLineRange, ITextSearchContext, ITextSearchResult, SearchSortOrder } from 'vs/workbench/services/search/common/search'; +import { IFileMatch, IPatternInfo, ISearchComplete, ISearchProgressItem, ISearchConfigurationProperties, ISearchService, ITextQuery, ITextSearchPreviewOptions, ITextSearchMatch, ITextSearchStats, resultIsMatch, ISearchRange, OneLineRange, ITextSearchContext, ITextSearchResult, SearchSortOrder, SearchCompletionExitCode } from 'vs/workbench/services/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { overviewRulerFindMatchForeground, minimapFindMatch } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; @@ -968,6 +968,7 @@ export class SearchModel extends Disposable { readonly onReplaceTermChanged: Event = this._onReplaceTermChanged.event; private currentCancelTokenSource: CancellationTokenSource | null = null; + private searchCancelledForNewSearch: boolean = false; constructor( @ISearchService private readonly searchService: ISearchService, @@ -1016,7 +1017,7 @@ export class SearchModel extends Disposable { } search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { - this.cancelSearch(); + this.cancelSearch(true); this._searchQuery = query; @@ -1114,7 +1115,12 @@ export class SearchModel extends Disposable { private onSearchError(e: any, duration: number): void { if (errors.isPromiseCanceledError(e)) { - this.onSearchCompleted(null, duration); + this.onSearchCompleted( + this.searchCancelledForNewSearch + ? { exit: SearchCompletionExitCode.NewSearchStarted, results: [] } + : null, + duration); + this.searchCancelledForNewSearch = false; } } @@ -1133,8 +1139,9 @@ export class SearchModel extends Disposable { return this.configurationService.getValue('search'); } - cancelSearch(): boolean { + cancelSearch(cancelledForNewSearch = false): boolean { if (this.currentCancelTokenSource) { + this.searchCancelledForNewSearch = cancelledForNewSearch; this.currentCancelTokenSource.cancel(); return true; } diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 66cdb94e6e0..cdf0e788d75 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -77,6 +77,7 @@ export class SearchEditor extends BaseTextEditor { private searchHistoryDelayer: Delayer; private messageDisposables: IDisposable[] = []; private container: HTMLElement; + private searchModel: SearchModel; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -107,6 +108,8 @@ export class SearchEditor extends BaseTextEditor { this.inputFocusContextKey = InputBoxFocusedKey.bindTo(scopedContextKeyService); this.searchOperation = this._register(new LongRunningOperation(progressService)); this.searchHistoryDelayer = new Delayer(2000); + + this.searchModel = this._register(this.instantiationService.createInstance(SearchModel)); } createEditor(parent: HTMLElement) { @@ -326,6 +329,8 @@ export class SearchEditor extends BaseTextEditor { } private async doRunSearch() { + this.searchModel.cancelSearch(true); + const startInput = this.getInput(); this.searchHistoryDelayer.trigger(() => { @@ -372,30 +377,26 @@ export class SearchEditor extends BaseTextEditor { catch (err) { return; } - const searchModel = this.instantiationService.createInstance(SearchModel); + this.searchOperation.start(500); - await searchModel.search(query).finally(() => this.searchOperation.stop()); + await this.searchModel.search(query).finally(() => this.searchOperation.stop()); const input = this.getInput(); if (!input || input !== startInput || JSON.stringify(config) !== JSON.stringify(this.readConfigFromWidget())) { - - searchModel.dispose(); return; } const controller = ReferencesController.get(this.searchResultEditor); controller.closeWidget(false); const labelFormatter = (uri: URI): string => this.labelService.getUriLabel(uri, { relative: true }); - const results = serializeSearchResultForEditor(searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, false); + const results = serializeSearchResultForEditor(this.searchModel.searchResult, config.includes, config.excludes, config.contextLines, labelFormatter, false); const { header, body } = await input.getModels(); this.modelService.updateModel(body, results.text); header.setValue(serializeSearchConfiguration(config)); input.setDirty(input.resource.scheme !== 'search-editor'); input.setMatchRanges(results.matchRanges); - - searchModel.dispose(); } layout(dimension: DOM.Dimension) { diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 99ba52ad15f..f8a834f47fe 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -198,6 +198,12 @@ export interface ISearchCompleteStats { export interface ISearchComplete extends ISearchCompleteStats { results: IFileMatch[]; + exit?: SearchCompletionExitCode +} + +export const enum SearchCompletionExitCode { + Normal, + NewSearchStarted } export interface ITextSearchStats {