提交 8b0f436d 编写于 作者: J Jackson Kearl

Refactor search delaying logic to be centrally controlled by the search...

Refactor search delaying logic to be centrally controlled by the search view/editor as opposed to each component input.
Fix #91031.
上级 8d87a32b
......@@ -10,15 +10,12 @@ import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { IInputValidator, HistoryInputBox, IInputBoxStyles } from 'vs/base/browser/ui/inputbox/inputBox';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachInputBoxStyler, attachCheckboxStyler } from 'vs/platform/theme/common/styler';
import { ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search';
import { Delayer } from 'vs/base/common/async';
import type { IThemable } from 'vs/base/common/styler';
export interface IOptions {
......@@ -50,11 +47,8 @@ export class PatternInputWidget extends Widget implements IThemable {
private _onCancel = this._register(new Emitter<void>());
onCancel: CommonEvent<void> = this._onCancel.event;
private searchOnTypeDelayer: Delayer<void>;
constructor(parent: HTMLElement, private contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null),
@IThemeService protected themeService: IThemeService,
@IConfigurationService private configurationService: IConfigurationService,
@IContextKeyService private readonly contextKeyService: IContextKeyService
) {
super();
......@@ -62,8 +56,6 @@ export class PatternInputWidget extends Widget implements IThemable {
this.placeholder = options.placeholder || '';
this.ariaLabel = options.ariaLabel || nls.localize('defaultLabel', "input");
this._register(this.searchOnTypeDelayer = new Delayer(this.searchConfig.searchOnTypeDebouncePeriod));
this.render(options);
parent.appendChild(this.domNode);
......@@ -152,6 +144,8 @@ export class PatternInputWidget extends Widget implements IThemable {
history: options.history || []
}, this.contextKeyService);
this._register(attachInputBoxStyler(this.inputBox, this.themeService));
this._register(this.inputBox.onDidChange(() => this._onSubmit.fire(true)));
this.inputFocusTracker = dom.trackFocus(this.inputBox.inputElement);
this.onkeyup(this.inputBox.inputElement, (keyboardEvent) => this.onInputKeyUp(keyboardEvent));
......@@ -170,24 +164,13 @@ export class PatternInputWidget extends Widget implements IThemable {
switch (keyboardEvent.keyCode) {
case KeyCode.Enter:
this.onSearchSubmit();
this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(false), 0);
this._onSubmit.fire(false);
return;
case KeyCode.Escape:
this._onCancel.fire();
return;
case KeyCode.Tab: case KeyCode.Tab | KeyMod.Shift: return;
default:
if (this.searchConfig.searchOnType) {
this._onCancel.fire();
this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(true), this.searchConfig.searchOnTypeDebouncePeriod);
}
return;
}
}
private get searchConfig() {
return this.configurationService.getValue<ISearchConfigurationProperties>('search');
}
}
export class ExcludePatternInputWidget extends PatternInputWidget {
......@@ -197,10 +180,9 @@ export class ExcludePatternInputWidget extends PatternInputWidget {
constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options: IOptions = Object.create(null),
@IThemeService themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService
) {
super(parent, contextViewProvider, options, themeService, configurationService, contextKeyService);
super(parent, contextViewProvider, options, themeService, contextKeyService);
}
private useExcludesAndIgnoreFilesBox!: Checkbox;
......
......@@ -283,7 +283,7 @@ export class RefreshAction extends Action {
run(): Promise<void> {
const searchView = getSearchView(this.viewsService);
if (searchView) {
searchView.onQueryChanged(false);
searchView.triggerQueryChange({ preserveFocus: false });
}
return Promise.resolve();
......
......@@ -145,6 +145,9 @@ export class SearchView extends ViewPane {
private toggleCollapseStateDelayer: Delayer<void>;
private triggerQueryDelayer: Delayer<void>;
private pauseSearching = false;
constructor(
options: IViewPaneOptions,
@IFileService private readonly fileService: IFileService,
......@@ -221,6 +224,7 @@ export class SearchView extends ViewPane {
this.addToSearchHistoryDelayer = this._register(new Delayer<void>(2000));
this.toggleCollapseStateDelayer = this._register(new Delayer<void>(100));
this.triggerQueryDelayer = this._register(new Delayer<void>(0));
const collapseDeepestExpandedLevelAction = this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL);
const expandAllAction = this.instantiationService.createInstance(ExpandAllAction, ExpandAllAction.ID, ExpandAllAction.LABEL);
......@@ -315,7 +319,7 @@ export class SearchView extends ViewPane {
this.inputPatternIncludes.setValue(patternIncludes);
this.inputPatternIncludes.onSubmit(triggeredOnType => this.onQueryChanged(true, triggeredOnType));
this.inputPatternIncludes.onSubmit(triggeredOnType => this.triggerQueryChange({ triggeredOnType, delay: this.searchConfig.searchOnTypeDebouncePeriod }));
this.inputPatternIncludes.onCancel(() => this.cancelSearch(false));
this.trackInputBox(this.inputPatternIncludes.inputFocusTracker, this.inputPatternIncludesFocused);
......@@ -331,9 +335,9 @@ export class SearchView extends ViewPane {
this.inputPatternExcludes.setValue(patternExclusions);
this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(useExcludesAndIgnoreFiles);
this.inputPatternExcludes.onSubmit(triggeredOnType => this.onQueryChanged(true, triggeredOnType));
this.inputPatternExcludes.onSubmit(triggeredOnType => this.triggerQueryChange({ triggeredOnType, delay: this.searchConfig.searchOnTypeDebouncePeriod }));
this.inputPatternExcludes.onCancel(() => this.cancelSearch(false));
this.inputPatternExcludes.onChangeIgnoreBox(() => this.onQueryChanged(true));
this.inputPatternExcludes.onChangeIgnoreBox(() => this.triggerQueryChange());
this.trackInputBox(this.inputPatternExcludes.inputFocusTracker, this.inputPatternExclusionsFocused);
this.messagesElement = dom.append(this.container, $('.messages'));
......@@ -436,9 +440,9 @@ export class SearchView extends ViewPane {
this.searchWidget.toggleReplace(true);
}
this._register(this.searchWidget.onSearchSubmit(triggeredOnType => this.onQueryChanged(true, triggeredOnType)));
this._register(this.searchWidget.onSearchSubmit(options => this.triggerQueryChange(options)));
this._register(this.searchWidget.onSearchCancel(({ focus }) => this.cancelSearch(focus)));
this._register(this.searchWidget.searchInput.onDidOptionChange(() => this.onQueryChanged(true)));
this._register(this.searchWidget.searchInput.onDidOptionChange(() => this.triggerQueryChange()));
this._register(this.searchWidget.onDidHeightChange(() => this.reLayout()));
......@@ -869,9 +873,11 @@ export class SearchView extends ViewPane {
if (this.searchWidget.searchInput.getRegex()) {
selectedText = strings.escapeRegExpCharacters(selectedText);
}
this.searchWidget.setValue(selectedText, true);
this.pauseSearching = true;
this.searchWidget.setValue(selectedText);
this.pauseSearching = false;
updatedText = true;
if (this.searchConfig.searchOnType) { this.onQueryChanged(false); }
if (this.searchConfig.searchOnType) { this.triggerQueryChange(); }
}
}
......@@ -1099,17 +1105,17 @@ export class SearchView extends ViewPane {
toggleCaseSensitive(): void {
this.searchWidget.searchInput.setCaseSensitive(!this.searchWidget.searchInput.getCaseSensitive());
this.onQueryChanged(true);
this.triggerQueryChange();
}
toggleWholeWords(): void {
this.searchWidget.searchInput.setWholeWords(!this.searchWidget.searchInput.getWholeWords());
this.onQueryChanged(true);
this.triggerQueryChange();
}
toggleRegex(): void {
this.searchWidget.searchInput.setRegex(!this.searchWidget.searchInput.getRegex());
this.onQueryChanged(true);
this.triggerQueryChange();
}
setSearchParameters(args: IFindInFilesArgs = {}): void {
......@@ -1139,7 +1145,7 @@ export class SearchView extends ViewPane {
}
}
if (typeof args.triggerSearch === 'boolean' && args.triggerSearch) {
this.onQueryChanged(true);
this.triggerQueryChange();
}
}
......@@ -1228,7 +1234,17 @@ export class SearchView extends ViewPane {
this.searchWidget.focus(false);
}
onQueryChanged(preserveFocus: boolean, triggeredOnType = false): void {
triggerQueryChange(_options?: { preserveFocus?: boolean, triggeredOnType?: boolean, delay?: number }) {
const options = { preserveFocus: true, triggeredOnType: false, delay: 0, ..._options };
if (!this.pauseSearching) {
this.triggerQueryDelayer.trigger(() => {
this._onQueryChanged(options.preserveFocus, options.triggeredOnType);
}, options.delay);
}
}
private _onQueryChanged(preserveFocus: boolean, triggeredOnType = false): void {
if (!this.searchWidget.searchInput.inputBox.isInputValid()) {
return;
}
......@@ -1409,7 +1425,7 @@ export class SearchView extends ViewPane {
const searchAgainLink = dom.append(p, $('a.pointer.prominent', undefined, nls.localize('rerunSearch.message', "Search again")));
this.messageDisposables.push(dom.addDisposableListener(searchAgainLink, dom.EventType.CLICK, (e: MouseEvent) => {
dom.EventHelper.stop(e, false);
this.onQueryChanged(false);
this.triggerQueryChange({ preserveFocus: false });
}));
} else if (hasIncludes || hasExcludes) {
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 {
this.inputPatternExcludes.setValue('');
this.inputPatternIncludes.setValue('');
this.onQueryChanged(false);
this.triggerQueryChange({ preserveFocus: false });
}));
} else {
const openSettingsLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.message', "Open Settings")));
......
......@@ -121,12 +121,11 @@ export class SearchWidget extends Widget {
private replaceActive: IContextKey<boolean>;
private replaceActionBar!: ActionBar;
private _replaceHistoryDelayer: Delayer<void>;
private _searchDelayer: Delayer<void>;
private ignoreGlobalFindBufferOnNextFocus = false;
private previousGlobalFindBufferValue: string | null = null;
private _onSearchSubmit = this._register(new Emitter<boolean>());
readonly onSearchSubmit: Event<boolean /* triggeredOnType */> = this._onSearchSubmit.event;
private _onSearchSubmit = this._register(new Emitter<{ triggeredOnType: boolean, delay: number }>());
readonly onSearchSubmit: Event<{ triggeredOnType: boolean, delay: number }> = this._onSearchSubmit.event;
private _onSearchCancel = this._register(new Emitter<{ focus: boolean }>());
readonly onSearchCancel: Event<{ focus: boolean }> = this._onSearchCancel.event;
......@@ -177,7 +176,6 @@ export class SearchWidget extends Widget {
this._replaceHistoryDelayer = new Delayer<void>(500);
this._searchDelayer = this._register(new Delayer<void>(this.searchConfiguration.searchOnTypeDebouncePeriod));
this.render(container, options);
this.configurationService.onDidChangeConfiguration(e => {
......@@ -447,10 +445,8 @@ export class SearchWidget extends Widget {
this._onReplaceToggled.fire();
}
setValue(value: string, skipSearchOnChange: boolean) {
this.temporarilySkipSearchOnChange = skipSearchOnChange;
setValue(value: string) {
this.searchInput.setValue(value);
this.temporarilySkipSearchOnChange = false;
}
setReplaceAllActionState(enabled: boolean): void {
......@@ -512,12 +508,12 @@ export class SearchWidget extends Widget {
matchienessHeuristic < 100 ? 5 : // expressions like `.` or `\w`
10; // only things matching empty string
this._searchDelayer.trigger((() => this.submitSearch(true)), this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier);
this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod * delayMultiplier);
} catch {
// pass
}
} else {
this._searchDelayer.trigger((() => this.submitSearch(true)), this.searchConfiguration.searchOnTypeDebouncePeriod);
this.submitSearch(true, this.searchConfiguration.searchOnTypeDebouncePeriod);
}
}
}
......@@ -628,7 +624,7 @@ export class SearchWidget extends Widget {
}
}
private submitSearch(triggeredOnType = false): void {
private submitSearch(triggeredOnType = false, delay: number = 0): void {
this.searchInput.validate();
if (!this.searchInput.inputBox.isInputValid()) {
return;
......@@ -639,7 +635,7 @@ export class SearchWidget extends Widget {
if (value && useGlobalFindBuffer) {
this.clipboardServce.writeFindText(value);
}
this._onSearchSubmit.fire(triggeredOnType);
this._onSearchSubmit.fire({ triggeredOnType, delay });
}
contextLines() {
......
......@@ -68,7 +68,7 @@ export class SearchEditor extends BaseTextEditor {
private toggleQueryDetailsButton!: HTMLElement;
private messageBox!: HTMLElement;
private runSearchDelayer = new Delayer(300);
private runSearchDelayer = new Delayer(0);
private pauseSearching: boolean = false;
private showingIncludesExcludes: boolean = false;
private inSearchEditorContextKey: IContextKey<boolean>;
......@@ -121,9 +121,9 @@ export class SearchEditor extends BaseTextEditor {
this.queryEditorWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.queryEditorContainer, { _hideReplaceToggle: true, showContextToggle: true }));
this._register(this.queryEditorWidget.onReplaceToggled(() => this.reLayout()));
this._register(this.queryEditorWidget.onDidHeightChange(() => this.reLayout()));
this.queryEditorWidget.onSearchSubmit(() => this.runSearch(true, true)); // onSearchSubmit has an internal delayer, so skip over ours.
this.queryEditorWidget.searchInput.onDidOptionChange(() => this.runSearch(false));
this.queryEditorWidget.onDidToggleContext(() => this.runSearch(false));
this.queryEditorWidget.onSearchSubmit(({ delay }) => this.triggerSearch({ delay }));
this.queryEditorWidget.searchInput.onDidOptionChange(() => this.triggerSearch({ resetCursor: false }));
this.queryEditorWidget.onDidToggleContext(() => this.triggerSearch({ resetCursor: false }));
// Includes/Excludes Dropdown
this.includesExcludesContainer = DOM.append(this.queryEditorContainer, DOM.$('.includes-excludes'));
......@@ -161,7 +161,7 @@ export class SearchEditor extends BaseTextEditor {
this.inputPatternIncludes = this._register(this.instantiationService.createInstance(PatternInputWidget, folderIncludesList, this.contextViewService, {
ariaLabel: localize('label.includes', 'Search Include Patterns'),
}));
this.inputPatternIncludes.onSubmit(_triggeredOnType => this.runSearch());
this.inputPatternIncludes.onSubmit(triggeredOnType => this.triggerSearch({ resetCursor: false, delay: triggeredOnType ? this.searchConfig.searchOnTypeDebouncePeriod : 0 }));
// // Excludes
const excludesList = DOM.append(this.includesExcludesContainer, DOM.$('.file-types.excludes'));
......@@ -170,8 +170,8 @@ export class SearchEditor extends BaseTextEditor {
this.inputPatternExcludes = this._register(this.instantiationService.createInstance(ExcludePatternInputWidget, excludesList, this.contextViewService, {
ariaLabel: localize('label.excludes', 'Search Exclude Patterns'),
}));
this.inputPatternExcludes.onSubmit(_triggeredOnType => this.runSearch());
this.inputPatternExcludes.onChangeIgnoreBox(() => this.runSearch());
this.inputPatternExcludes.onSubmit(triggeredOnType => this.triggerSearch({ resetCursor: false, delay: triggeredOnType ? this.searchConfig.searchOnTypeDebouncePeriod : 0 }));
this.inputPatternExcludes.onChangeIgnoreBox(() => this.triggerSearch());
[this.queryEditorWidget.searchInput, this.inputPatternIncludes, this.inputPatternExcludes].map(input =>
this._register(attachInputBoxStyler(input, this.themeService, { inputBorder: searchEditorTextInputBorder })));
......@@ -188,7 +188,7 @@ export class SearchEditor extends BaseTextEditor {
if (show) {
const runAgainLink = DOM.append(this.messageBox, DOM.$('a.pointer.prominent.message', {}, localize('runSearch', "Run Search")));
this.messageDisposables.push(DOM.addDisposableListener(runAgainLink, DOM.EventType.CLICK, async () => {
await this.runSearch(true, true);
await this.triggerSearch();
this.toggleRunAgainMessage(false);
}));
}
......@@ -271,17 +271,17 @@ export class SearchEditor extends BaseTextEditor {
toggleWholeWords() {
this.queryEditorWidget.searchInput.setWholeWords(!this.queryEditorWidget.searchInput.getWholeWords());
this.runSearch(false);
this.triggerSearch({ resetCursor: false });
}
toggleRegex() {
this.queryEditorWidget.searchInput.setRegex(!this.queryEditorWidget.searchInput.getRegex());
this.runSearch(false);
this.triggerSearch({ resetCursor: false });
}
toggleCaseSensitive() {
this.queryEditorWidget.searchInput.setCaseSensitive(!this.queryEditorWidget.searchInput.getCaseSensitive());
this.runSearch(false);
this.triggerSearch({ resetCursor: false });
}
toggleContextLines() {
......@@ -292,16 +292,22 @@ export class SearchEditor extends BaseTextEditor {
this.toggleIncludesExcludes();
}
async runSearch(resetCursor = true, instant = false) {
private get searchConfig(): ISearchConfigurationProperties {
return this.configurationService.getValue<ISearchConfigurationProperties>('search');
}
async triggerSearch(_options?: { resetCursor?: boolean; delay?: number; }) {
const options = { resetCursor: true, delay: 0, ..._options };
if (!this.pauseSearching) {
await this.runSearchDelayer.trigger(async () => {
await this.doRunSearch();
this.toggleRunAgainMessage(false);
if (resetCursor) {
if (options.resetCursor) {
this.searchResultEditor.setSelection(new Range(1, 1, 1, 1));
this.searchResultEditor.setScrollPosition({ scrollTop: 0, scrollLeft: 0 });
}
}, instant ? 0 : undefined);
}, options.delay);
}
}
......@@ -431,7 +437,7 @@ export class SearchEditor extends BaseTextEditor {
const config = extractSearchQuery(header);
this.toggleRunAgainMessage(body.getLineCount() === 1 && body.getValue() === '' && config.query !== '');
this.queryEditorWidget.setValue(config.query, true);
this.queryEditorWidget.setValue(config.query);
this.queryEditorWidget.searchInput.setCaseSensitive(config.caseSensitive);
this.queryEditorWidget.searchInput.setRegex(config.regexp);
this.queryEditorWidget.searchInput.setWholeWords(config.wholeWord);
......
......@@ -147,7 +147,7 @@ const openNewSearchEditor =
const editor = await editorService.openEditor(input, { pinned: true }) as SearchEditor;
if (selected && configurationService.getValue<ISearchConfigurationProperties>('search').searchOnType) {
editor.runSearch(true, true);
editor.triggerSearch();
}
};
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册