Skip to content

Commit 8b0f436

Browse files
author
Jackson Kearl
committed
Refactor search delaying logic to be centrally controlled by the search view/editor as opposed to each component input.
Fix microsoft#91031.
1 parent 8d87a32 commit 8b0f436

6 files changed

Lines changed: 65 additions & 65 deletions

File tree

src/vs/workbench/contrib/search/browser/patternInputWidget.ts

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,12 @@ import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
1010
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
1111
import { IInputValidator, HistoryInputBox, IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
1212
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
13-
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
13+
import { KeyCode } from 'vs/base/common/keyCodes';
1414
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
1515
import { IThemeService } from 'vs/platform/theme/common/themeService';
1616
import { attachInputBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler';
1717
import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
1818
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
19-
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
20-
import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search';
21-
import { Delayer } from 'vs/base/common/async';
2219
import type { IThemable } from 'vs/base/common/styler';
2320

2421
export interface IOptions {
@@ -50,20 +47,15 @@ export class PatternInputWidget extends Widget implements IThemable {
5047
private _onCancel = this._register(new Emitter<void>());
5148
onCancel: CommonEvent<void> = this._onCancel.event;
5249

53-
private searchOnTypeDelayer: Delayer<void>;
54-
5550
constructor(parent: HTMLElement, private contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null),
5651
@IThemeService protected themeService: IThemeService,
57-
@IConfigurationService private configurationService: IConfigurationService,
5852
@IContextKeyService private readonly contextKeyService: IContextKeyService
5953
) {
6054
super();
6155
this.width = options.width || 100;
6256
this.placeholder = options.placeholder || '';
6357
this.ariaLabel = options.ariaLabel || nls.localize('defaultLabel', "input");
6458

65-
this._register(this.searchOnTypeDelayer = new Delayer(this.searchConfig.searchOnTypeDebouncePeriod));
66-
6759
this.render(options);
6860

6961
parent.appendChild(this.domNode);
@@ -152,6 +144,8 @@ export class PatternInputWidget extends Widget implements IThemable {
152144
history: options.history || []
153145
}, this.contextKeyService);
154146
this._register(attachInputBoxStyler(this.inputBox, this.themeService));
147+
this._register(this.inputBox.onDidChange(() => this._onSubmit.fire(true)));
148+
155149
this.inputFocusTracker = dom.trackFocus(this.inputBox.inputElement);
156150
this.onkeyup(this.inputBox.inputElement, (keyboardEvent) => this.onInputKeyUp(keyboardEvent));
157151

@@ -170,24 +164,13 @@ export class PatternInputWidget extends Widget implements IThemable {
170164
switch (keyboardEvent.keyCode) {
171165
case KeyCode.Enter:
172166
this.onSearchSubmit();
173-
this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(false), 0);
167+
this._onSubmit.fire(false);
174168
return;
175169
case KeyCode.Escape:
176170
this._onCancel.fire();
177171
return;
178-
case KeyCode.Tab: case KeyCode.Tab | KeyMod.Shift: return;
179-
default:
180-
if (this.searchConfig.searchOnType) {
181-
this._onCancel.fire();
182-
this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(true), this.searchConfig.searchOnTypeDebouncePeriod);
183-
}
184-
return;
185172
}
186173
}
187-
188-
private get searchConfig() {
189-
return this.configurationService.getValue<ISearchConfigurationProperties>('search');
190-
}
191174
}
192175

193176
export class ExcludePatternInputWidget extends PatternInputWidget {
@@ -197,10 +180,9 @@ export class ExcludePatternInputWidget extends PatternInputWidget {
197180

198181
constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null),
199182
@IThemeService themeService: IThemeService,
200-
@IConfigurationService configurationService: IConfigurationService,
201183
@IContextKeyService contextKeyService: IContextKeyService
202184
) {
203-
super(parent, contextViewProvider, options, themeService, configurationService, contextKeyService);
185+
super(parent, contextViewProvider, options, themeService, contextKeyService);
204186
}
205187

206188
private useExcludesAndIgnoreFilesBox!: Checkbox;

src/vs/workbench/contrib/search/browser/searchActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ export class RefreshAction extends Action {
283283
run(): Promise<void> {
284284
const searchView = getSearchView(this.viewsService);
285285
if (searchView) {
286-
searchView.onQueryChanged(false);
286+
searchView.triggerQueryChange({ preserveFocus: false });
287287
}
288288

289289
return Promise.resolve();

src/vs/workbench/contrib/search/browser/searchView.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ export class SearchView extends ViewPane {
145145

146146
private toggleCollapseStateDelayer: Delayer<void>;
147147

148+
private triggerQueryDelayer: Delayer<void>;
149+
private pauseSearching = false;
150+
148151
constructor(
149152
options: IViewPaneOptions,
150153
@IFileService private readonly fileService: IFileService,
@@ -221,6 +224,7 @@ export class SearchView extends ViewPane {
221224

222225
this.addToSearchHistoryDelayer = this._register(new Delayer<void>(2000));
223226
this.toggleCollapseStateDelayer = this._register(new Delayer<void>(100));
227+
this.triggerQueryDelayer = this._register(new Delayer<void>(0));
224228

225229
const collapseDeepestExpandedLevelAction = this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL);
226230
const expandAllAction = this.instantiationService.createInstance(ExpandAllAction, ExpandAllAction.ID, ExpandAllAction.LABEL);
@@ -315,7 +319,7 @@ export class SearchView extends ViewPane {
315319

316320
this.inputPatternIncludes.setValue(patternIncludes);
317321

318-
this.inputPatternIncludes.onSubmit(triggeredOnType => this.onQueryChanged(true, triggeredOnType));
322+
this.inputPatternIncludes.onSubmit(triggeredOnType => this.triggerQueryChange({ triggeredOnType, delay: this.searchConfig.searchOnTypeDebouncePeriod }));
319323
this.inputPatternIncludes.onCancel(() => this.cancelSearch(false));
320324
this.trackInputBox(this.inputPatternIncludes.inputFocusTracker, this.inputPatternIncludesFocused);
321325

@@ -331,9 +335,9 @@ export class SearchView extends ViewPane {
331335
this.inputPatternExcludes.setValue(patternExclusions);
332336
this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(useExcludesAndIgnoreFiles);
333337

334-
this.inputPatternExcludes.onSubmit(triggeredOnType => this.onQueryChanged(true, triggeredOnType));
338+
this.inputPatternExcludes.onSubmit(triggeredOnType => this.triggerQueryChange({ triggeredOnType, delay: this.searchConfig.searchOnTypeDebouncePeriod }));
335339
this.inputPatternExcludes.onCancel(() => this.cancelSearch(false));
336-
this.inputPatternExcludes.onChangeIgnoreBox(() => this.onQueryChanged(true));
340+
this.inputPatternExcludes.onChangeIgnoreBox(() => this.triggerQueryChange());
337341
this.trackInputBox(this.inputPatternExcludes.inputFocusTracker, this.inputPatternExclusionsFocused);
338342

339343
this.messagesElement = dom.append(this.container, $('.messages'));
@@ -436,9 +440,9 @@ export class SearchView extends ViewPane {
436440
this.searchWidget.toggleReplace(true);
437441
}
438442

439-
this._register(this.searchWidget.onSearchSubmit(triggeredOnType => this.onQueryChanged(true, triggeredOnType)));
443+
this._register(this.searchWidget.onSearchSubmit(options => this.triggerQueryChange(options)));
440444
this._register(this.searchWidget.onSearchCancel(({ focus }) => this.cancelSearch(focus)));
441-
this._register(this.searchWidget.searchInput.onDidOptionChange(() => this.onQueryChanged(true)));
445+
this._register(this.searchWidget.searchInput.onDidOptionChange(() => this.triggerQueryChange()));
442446

443447
this._register(this.searchWidget.onDidHeightChange(() => this.reLayout()));
444448

@@ -869,9 +873,11 @@ export class SearchView extends ViewPane {
869873
if (this.searchWidget.searchInput.getRegex()) {
870874
selectedText = strings.escapeRegExpCharacters(selectedText);
871875
}
872-
this.searchWidget.setValue(selectedText, true);
876+
this.pauseSearching = true;
877+
this.searchWidget.setValue(selectedText);
878+
this.pauseSearching = false;
873879
updatedText = true;
874-
if (this.searchConfig.searchOnType) { this.onQueryChanged(false); }
880+
if (this.searchConfig.searchOnType) { this.triggerQueryChange(); }
875881
}
876882
}
877883

@@ -1099,17 +1105,17 @@ export class SearchView extends ViewPane {
10991105

11001106
toggleCaseSensitive(): void {
11011107
this.searchWidget.searchInput.setCaseSensitive(!this.searchWidget.searchInput.getCaseSensitive());
1102-
this.onQueryChanged(true);
1108+
this.triggerQueryChange();
11031109
}
11041110

11051111
toggleWholeWords(): void {
11061112
this.searchWidget.searchInput.setWholeWords(!this.searchWidget.searchInput.getWholeWords());
1107-
this.onQueryChanged(true);
1113+
this.triggerQueryChange();
11081114
}
11091115

11101116
toggleRegex(): void {
11111117
this.searchWidget.searchInput.setRegex(!this.searchWidget.searchInput.getRegex());
1112-
this.onQueryChanged(true);
1118+
this.triggerQueryChange();
11131119
}
11141120

11151121
setSearchParameters(args: IFindInFilesArgs = {}): void {
@@ -1139,7 +1145,7 @@ export class SearchView extends ViewPane {
11391145
}
11401146
}
11411147
if (typeof args.triggerSearch === 'boolean' && args.triggerSearch) {
1142-
this.onQueryChanged(true);
1148+
this.triggerQueryChange();
11431149
}
11441150
}
11451151

@@ -1228,7 +1234,17 @@ export class SearchView extends ViewPane {
12281234
this.searchWidget.focus(false);
12291235
}
12301236

1231-
onQueryChanged(preserveFocus: boolean, triggeredOnType = false): void {
1237+
triggerQueryChange(_options?: { preserveFocus?: boolean, triggeredOnType?: boolean, delay?: number }) {
1238+
const options = { preserveFocus: true, triggeredOnType: false, delay: 0, ..._options };
1239+
1240+
if (!this.pauseSearching) {
1241+
this.triggerQueryDelayer.trigger(() => {
1242+
this._onQueryChanged(options.preserveFocus, options.triggeredOnType);
1243+
}, options.delay);
1244+
}
1245+
}
1246+
1247+
private _onQueryChanged(preserveFocus: boolean, triggeredOnType = false): void {
12321248
if (!this.searchWidget.searchInput.inputBox.isInputValid()) {
12331249
return;
12341250
}
@@ -1409,7 +1425,7 @@ export class SearchView extends ViewPane {
14091425
const searchAgainLink = dom.append(p, $('a.pointer.prominent', undefined, nls.localize('rerunSearch.message', "Search again")));
14101426
this.messageDisposables.push(dom.addDisposableListener(searchAgainLink, dom.EventType.CLICK, (e: MouseEvent) => {
14111427
dom.EventHelper.stop(e, false);
1412-
this.onQueryChanged(false);
1428+
this.triggerQueryChange({ preserveFocus: false });
14131429
}));
14141430
} else if (hasIncludes || hasExcludes) {
14151431
const searchAgainLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('rerunSearchInAll.message', "Search again in all files")));
@@ -1419,7 +1435,7 @@ export class SearchView extends ViewPane {
14191435
this.inputPatternExcludes.setValue('');
14201436
this.inputPatternIncludes.setValue('');
14211437

1422-
this.onQueryChanged(false);
1438+
this.triggerQueryChange({ preserveFocus: false });
14231439
}));
14241440
} else {
14251441
const openSettingsLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.message', "Open Settings")));

src/vs/workbench/contrib/search/browser/searchWidget.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,11 @@ export class SearchWidget extends Widget {
121121
private replaceActive: IContextKey<boolean>;
122122
private replaceActionBar!: ActionBar;
123123
private _replaceHistoryDelayer: Delayer<void>;
124-
private _searchDelayer: Delayer<void>;
125124
private ignoreGlobalFindBufferOnNextFocus = false;
126125
private previousGlobalFindBufferValue: string | null = null;
127126

128-
private _onSearchSubmit = this._register(new Emitter<boolean>());
129-
readonly onSearchSubmit: Event<boolean /* triggeredOnType */> = this._onSearchSubmit.event;
127+
private _onSearchSubmit = this._register(new Emitter<{ triggeredOnType: boolean, delay: number }>());
128+
readonly onSearchSubmit: Event<{ triggeredOnType: boolean, delay: number }> = this._onSearchSubmit.event;
130129

131130
private _onSearchCancel = this._register(new Emitter<{ focus: boolean }>());
132131
readonly onSearchCancel: Event<{ focus: boolean }> = this._onSearchCancel.event;
@@ -177,7 +176,6 @@ export class SearchWidget extends Widget {
177176

178177
this._replaceHistoryDelayer = new Delayer<void>(500);
179178

180-
this._searchDelayer = this._register(new Delayer<void>(this.searchConfiguration.searchOnTypeDebouncePeriod));
181179
this.render(container, options);
182180

183181
this.configurationService.onDidChangeConfiguration(e => {
@@ -447,10 +445,8 @@ export class SearchWidget extends Widget {
447445
this._onReplaceToggled.fire();
448446
}
449447

450-
setValue(value: string, skipSearchOnChange: boolean) {
451-
this.temporarilySkipSearchOnChange = skipSearchOnChange;
448+
setValue(value: string) {
452449
this.searchInput.setValue(value);
453-
this.temporarilySkipSearchOnChange = false;
454450
}
455451

456452
setReplaceAllActionState(enabled: boolean): void {
@@ -512,12 +508,12 @@ export class SearchWidget extends Widget {
512508
matchienessHeuristic < 100 ? 5 : // expressions like `.` or `\w`
513509
10; // only things matching empty string
514510

515-
this._searchDelayer.trigger((() => this.submitSearch(true)), this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier);
511+
this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier);
516512
} catch {
517513
// pass
518514
}
519515
} else {
520-
this._searchDelayer.trigger((() => this.submitSearch(true)), this.searchConfiguration.searchOnTypeDebouncePeriod);
516+
this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod);
521517
}
522518
}
523519
}
@@ -628,7 +624,7 @@ export class SearchWidget extends Widget {
628624
}
629625
}
630626

631-
private submitSearch(triggeredOnType = false): void {
627+
private submitSearch(triggeredOnType = false, delay: number = 0): void {
632628
this.searchInput.validate();
633629
if (!this.searchInput.inputBox.isInputValid()) {
634630
return;
@@ -639,7 +635,7 @@ export class SearchWidget extends Widget {
639635
if (value && useGlobalFindBuffer) {
640636
this.clipboardServce.writeFindText(value);
641637
}
642-
this._onSearchSubmit.fire(triggeredOnType);
638+
this._onSearchSubmit.fire({ triggeredOnType, delay });
643639
}
644640

645641
contextLines() {

0 commit comments

Comments
 (0)