Skip to content

Commit bdbf134

Browse files
committed
1 parent eb45d35 commit bdbf134

6 files changed

Lines changed: 115 additions & 97 deletions

File tree

src/vs/base/browser/ui/findinput/findInput.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ export class FindInput extends Widget {
8383
private _onCaseSensitiveKeyDown = this._register(new Emitter<IKeyboardEvent>());
8484
public readonly onCaseSensitiveKeyDown: Event<IKeyboardEvent> = this._onCaseSensitiveKeyDown.event;
8585

86+
private _onRegexKeyDown = this._register(new Emitter<IKeyboardEvent>());
87+
public readonly onRegexKeyDown: Event<IKeyboardEvent> = this._onRegexKeyDown.event;
88+
8689
constructor(parent: HTMLElement, contextViewProvider: IContextViewProvider, options?: IFindInputOptions) {
8790
super();
8891
this.contextViewProvider = contextViewProvider;
@@ -248,6 +251,10 @@ export class FindInput extends Widget {
248251
this.caseSensitive.focus();
249252
}
250253

254+
public focusOnRegex(): void {
255+
this.regex.focus();
256+
}
257+
251258
private _lastHighlightFindOptions: number = 0;
252259
public highlightFindOptions(): void {
253260
dom.removeClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions));
@@ -294,6 +301,9 @@ export class FindInput extends Widget {
294301
this.setInputWidth();
295302
this.validate();
296303
},
304+
onKeyDown: (e) => {
305+
this._onRegexKeyDown.fire(e);
306+
},
297307
inputActiveOptionBorder: this.inputActiveOptionBorder
298308
}));
299309
this.wholeWords = this._register(new WholeWordsCheckbox({

src/vs/platform/widget/browser/widget.contribution.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
'use strict';
66

77
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
8-
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
8+
import { KeyCode } from 'vs/base/common/keyCodes';
99
import { ContextKeyDefinedExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
1010
import { HistoryInputBoxContext } from 'vs/platform/widget/browser/input';
1111
import { HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputbox';
@@ -14,7 +14,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
1414
id: 'input.action.historyPrevious',
1515
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
1616
when: new ContextKeyDefinedExpr(HistoryInputBoxContext),
17-
primary: KeyMod.Alt | KeyCode.UpArrow,
17+
primary: KeyCode.UpArrow,
1818
handler: (accessor, arg2) => {
1919
const historyInputBox: HistoryInputBox = accessor.get(IContextKeyService).getContext(document.activeElement).getValue(HistoryInputBoxContext);
2020
historyInputBox.showPreviousValue();
@@ -25,7 +25,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
2525
id: 'input.action.historyNext',
2626
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
2727
when: new ContextKeyDefinedExpr(HistoryInputBoxContext),
28-
primary: KeyMod.Alt | KeyCode.DownArrow,
28+
primary: KeyCode.DownArrow,
2929
handler: (accessor, arg2) => {
3030
const historyInputBox: HistoryInputBox = accessor.get(IContextKeyService).getContext(document.activeElement).getValue(HistoryInputBoxContext);
3131
historyInputBox.showNextValue();

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

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -251,42 +251,6 @@ export class ShowPreviousReplaceTermAction extends Action {
251251
}
252252
}
253253

254-
export class FocusNextInputAction extends Action {
255-
256-
public static readonly ID = 'search.focus.nextInputBox';
257-
258-
constructor(id: string, label: string,
259-
@IViewletService private viewletService: IViewletService,
260-
@IPanelService private panelService: IPanelService
261-
) {
262-
super(id, label);
263-
}
264-
265-
public run(): TPromise<any> {
266-
const searchView = getSearchView(this.viewletService, this.panelService);
267-
searchView.focusNextInputBox();
268-
return TPromise.as(null);
269-
}
270-
}
271-
272-
export class FocusPreviousInputAction extends Action {
273-
274-
public static readonly ID = 'search.focus.previousInputBox';
275-
276-
constructor(id: string, label: string,
277-
@IViewletService private viewletService: IViewletService,
278-
@IPanelService private panelService: IPanelService
279-
) {
280-
super(id, label);
281-
}
282-
283-
public run(): TPromise<any> {
284-
const searchView = getSearchView(this.viewletService, this.panelService);
285-
searchView.focusPreviousInputBox();
286-
return TPromise.as(null);
287-
}
288-
}
289-
290254
export const FocusActiveEditorCommand = (accessor: ServicesAccessor) => {
291255
const editorService = accessor.get(IEditorService);
292256
const activeControl = editorService.activeControl;

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { IAction } from 'vs/base/common/actions';
1515
import { Delayer } from 'vs/base/common/async';
1616
import * as errors from 'vs/base/common/errors';
1717
import { debounceEvent, Emitter } from 'vs/base/common/event';
18-
import { KeyCode } from 'vs/base/common/keyCodes';
18+
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
1919
import * as paths from 'vs/base/common/paths';
2020
import * as env from 'vs/base/common/platform';
2121
import * as strings from 'vs/base/common/strings';
@@ -98,6 +98,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
9898
private searchWidget: SearchWidget;
9999
private size: dom.Dimension;
100100
private queryDetails: HTMLElement;
101+
private toggleQueryDetailsButton: HTMLElement;
101102
private inputPatternExcludes: ExcludePatternInputWidget;
102103
private inputPatternIncludes: PatternInputWidget;
103104
private results: Builder;
@@ -222,7 +223,7 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
222223
}
223224

224225
this.queryDetails = this.searchWidgetsContainer.div({ 'class': ['query-details'] }, (builder) => {
225-
builder.div({ 'class': 'more', 'tabindex': 0, 'role': 'button', 'title': nls.localize('moreSearch', "Toggle Search Details") })
226+
this.toggleQueryDetailsButton = builder.div({ 'class': 'more', 'tabindex': 0, 'role': 'button', 'title': nls.localize('moreSearch', "Toggle Search Details") })
226227
.on(dom.EventType.CLICK, (e) => {
227228
dom.EventHelper.stop(e);
228229
this.toggleQueryDetails();
@@ -233,7 +234,18 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
233234
dom.EventHelper.stop(e);
234235
this.toggleQueryDetails(false);
235236
}
236-
});
237+
}).on(dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
238+
let event = new StandardKeyboardEvent(e);
239+
240+
if (event.equals(KeyMod.Shift | KeyCode.Tab)) {
241+
if (this.searchWidget.isReplaceActive()) {
242+
this.searchWidget.focusReplaceAllAction();
243+
} else {
244+
this.searchWidget.focusRegexAction();
245+
}
246+
dom.EventHelper.stop(e);
247+
}
248+
}).getHTMLElement();
237249

238250
//folder includes list
239251
builder.div({ 'class': 'file-types includes' }, (builder) => {
@@ -360,6 +372,10 @@ export class SearchView extends Viewlet implements IViewlet, IPanel {
360372
this.delayedRefresh.trigger(() => this.tree.refresh());
361373
}));
362374

375+
this.toUnbind.push(this.searchWidget.onBlur(() => {
376+
this.toggleQueryDetailsButton.focus();
377+
}));
378+
363379
this.toUnbind.push(this.searchWidget.onReplaceAll(() => this.replaceAll()));
364380
this.trackInputBox(this.searchWidget.searchInputFocusTracker);
365381
this.trackInputBox(this.searchWidget.replaceInputFocusTracker);

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

Lines changed: 82 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,23 @@ export class SearchWidget extends Widget {
116116
private _onReplaceAll = this._register(new Emitter<void>());
117117
public readonly onReplaceAll: Event<void> = this._onReplaceAll.event;
118118

119+
private _onBlur = this._register(new Emitter<void>());
120+
public readonly onBlur: Event<void> = this._onBlur.event;
121+
119122
constructor(
120123
container: Builder,
121124
options: ISearchWidgetOptions,
122125
@IContextViewService private contextViewService: IContextViewService,
123126
@IThemeService private themeService: IThemeService,
124-
@IContextKeyService private keyBindingService: IContextKeyService,
125-
@IKeybindingService private keyBindingService2: IKeybindingService,
127+
@IContextKeyService private contextKeyService: IContextKeyService,
128+
@IKeybindingService private keyBindingService: IKeybindingService,
126129
@IClipboardService private clipboardServce: IClipboardService,
127130
@IConfigurationService private configurationService: IConfigurationService
128131
) {
129132
super();
130-
this.replaceActive = Constants.ReplaceActiveKey.bindTo(this.keyBindingService);
131-
this.searchInputBoxFocused = Constants.SearchInputBoxFocusedKey.bindTo(this.keyBindingService);
132-
this.replaceInputBoxFocused = Constants.ReplaceInputBoxFocusedKey.bindTo(this.keyBindingService);
133+
this.replaceActive = Constants.ReplaceActiveKey.bindTo(this.contextKeyService);
134+
this.searchInputBoxFocused = Constants.SearchInputBoxFocusedKey.bindTo(this.contextKeyService);
135+
this.replaceInputBoxFocused = Constants.ReplaceInputBoxFocusedKey.bindTo(this.contextKeyService);
133136
this.render(container, options);
134137
}
135138

@@ -164,6 +167,10 @@ export class SearchWidget extends Widget {
164167
return !dom.hasClass(this.replaceContainer, 'disabled');
165168
}
166169

170+
isReplaceActive(): boolean {
171+
return this.replaceActive.get();
172+
}
173+
167174
public getReplaceValue(): string {
168175
return this.replaceInput.value;
169176
}
@@ -210,6 +217,14 @@ export class SearchWidget extends Widget {
210217
return this.replaceInput.hasFocus();
211218
}
212219

220+
public focusReplaceAllAction(): void {
221+
this.replaceActionBar.focus(true);
222+
}
223+
224+
public focusRegexAction(): void {
225+
this.searchInput.focusOnRegex();
226+
}
227+
213228
private render(container: Builder, options: ISearchWidgetOptions): void {
214229
this.domNode = container.div({ 'class': 'search-widget' }).style({ position: 'relative' }).getHTMLElement();
215230
this.renderToggleReplaceButton(this.domNode);
@@ -237,23 +252,25 @@ export class SearchWidget extends Widget {
237252
label: nls.localize('label.Search', 'Search: Type Search Term and press Enter to search or Escape to cancel'),
238253
validation: (value: string) => this.validateSearchInput(value),
239254
placeholder: nls.localize('search.placeHolder', "Search"),
240-
appendCaseSensitiveLabel: appendKeyBindingLabel('', this.keyBindingService2.lookupKeybinding(Constants.ToggleCaseSensitiveCommandId), this.keyBindingService2),
241-
appendWholeWordsLabel: appendKeyBindingLabel('', this.keyBindingService2.lookupKeybinding(Constants.ToggleWholeWordCommandId), this.keyBindingService2),
242-
appendRegexLabel: appendKeyBindingLabel('', this.keyBindingService2.lookupKeybinding(Constants.ToggleRegexCommandId), this.keyBindingService2),
255+
appendCaseSensitiveLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleCaseSensitiveCommandId), this.keyBindingService),
256+
appendWholeWordsLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleWholeWordCommandId), this.keyBindingService),
257+
appendRegexLabel: appendKeyBindingLabel('', this.keyBindingService.lookupKeybinding(Constants.ToggleRegexCommandId), this.keyBindingService),
243258
history: options.searchHistory
244259
};
245260

246261
let searchInputContainer = dom.append(parent, dom.$('.search-container.input-box'));
247-
this.searchInput = this._register(new ContextScopedFindInput(searchInputContainer, this.contextViewService, inputOptions, this.keyBindingService));
262+
this.searchInput = this._register(new ContextScopedFindInput(searchInputContainer, this.contextViewService, inputOptions, this.contextKeyService));
248263
this._register(attachFindInputBoxStyler(this.searchInput, this.themeService));
249-
this.searchInput.onKeyUp((keyboardEvent: IKeyboardEvent) => this.onSearchInputKeyUp(keyboardEvent));
264+
this.searchInput.onKeyDown((keyboardEvent: IKeyboardEvent) => this.onSearchInputKeyDown(keyboardEvent));
250265
this.searchInput.setValue(options.value || '');
251266
this.searchInput.setRegex(!!options.isRegex);
252267
this.searchInput.setCaseSensitive(!!options.isCaseSensitive);
253268
this.searchInput.setWholeWords(!!options.isWholeWords);
254269
this._register(this.onSearchSubmit(() => {
255270
this.searchInput.inputBox.addToHistory(this.searchInput.getValue());
256271
}));
272+
this.searchInput.onCaseSensitiveKeyDown((keyboardEvent: IKeyboardEvent) => this.onCaseSensitiveKeyDown(keyboardEvent));
273+
this.searchInput.onRegexKeyDown((keyboardEvent: IKeyboardEvent) => this.onRegexKeyDown(keyboardEvent));
257274

258275
this._register(this.onReplaceValueChanged(() => {
259276
this.replaceInput.addToHistory(this.replaceInput.value);
@@ -287,9 +304,9 @@ export class SearchWidget extends Widget {
287304
ariaLabel: nls.localize('label.Replace', 'Replace: Type replace term and press Enter to preview or Escape to cancel'),
288305
placeholder: nls.localize('search.replace.placeHolder', "Replace"),
289306
history: options.replaceHistory || []
290-
}, this.keyBindingService));
307+
}, this.contextKeyService));
291308
this._register(attachInputBoxStyler(this.replaceInput, this.themeService));
292-
this.onkeyup(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyUp(keyboardEvent));
309+
this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent));
293310
this.replaceInput.onDidChange(() => this._onReplaceValueChanged.fire());
294311
this.searchInput.inputBox.onDidChange(() => this.onSearchInputChanged());
295312

@@ -320,15 +337,11 @@ export class SearchWidget extends Widget {
320337
public setReplaceAllActionState(enabled: boolean): void {
321338
if (this.replaceAllAction.enabled !== enabled) {
322339
this.replaceAllAction.enabled = enabled;
323-
this.replaceAllAction.label = enabled ? SearchWidget.REPLACE_ALL_ENABLED_LABEL(this.keyBindingService2) : SearchWidget.REPLACE_ALL_DISABLED_LABEL;
340+
this.replaceAllAction.label = enabled ? SearchWidget.REPLACE_ALL_ENABLED_LABEL(this.keyBindingService) : SearchWidget.REPLACE_ALL_DISABLED_LABEL;
324341
this.updateReplaceActiveState();
325342
}
326343
}
327344

328-
private isReplaceActive(): boolean {
329-
return this.replaceActive.get();
330-
}
331-
332345
private updateReplaceActiveState(): void {
333346
let currentState = this.isReplaceActive();
334347
let newState = this.isReplaceShown() && this.replaceAllAction.enabled;
@@ -366,26 +379,61 @@ export class SearchWidget extends Widget {
366379
this.setReplaceAllActionState(false);
367380
}
368381

369-
private onSearchInputKeyUp(keyboardEvent: IKeyboardEvent) {
370-
switch (keyboardEvent.keyCode) {
371-
case KeyCode.Enter:
372-
this.submitSearch();
373-
return;
374-
case KeyCode.Escape:
375-
this._onSearchCancel.fire();
376-
return;
377-
default:
378-
return;
382+
private onSearchInputKeyDown(keyboardEvent: IKeyboardEvent) {
383+
if (keyboardEvent.equals(KeyCode.Enter)) {
384+
this.submitSearch();
385+
keyboardEvent.preventDefault();
386+
}
387+
388+
else if (keyboardEvent.equals(KeyCode.Escape)) {
389+
this._onSearchCancel.fire();
390+
keyboardEvent.preventDefault();
391+
}
392+
393+
else if (keyboardEvent.equals(KeyCode.Tab)) {
394+
if (this.isReplaceShown()) {
395+
this.replaceInput.focus();
396+
} else {
397+
this.searchInput.focusOnCaseSensitive();
398+
}
399+
keyboardEvent.preventDefault();
400+
}
401+
}
402+
403+
private onCaseSensitiveKeyDown(keyboardEvent: IKeyboardEvent) {
404+
if (keyboardEvent.equals(KeyMod.Shift | KeyCode.Tab)) {
405+
if (this.isReplaceShown()) {
406+
this.replaceInput.focus();
407+
keyboardEvent.preventDefault();
408+
}
409+
}
410+
}
411+
412+
private onRegexKeyDown(keyboardEvent: IKeyboardEvent) {
413+
if (keyboardEvent.equals(KeyCode.Tab)) {
414+
if (this.isReplaceActive()) {
415+
this.focusReplaceAllAction();
416+
} else {
417+
this._onBlur.fire();
418+
}
419+
keyboardEvent.preventDefault();
379420
}
380421
}
381422

382-
private onReplaceInputKeyUp(keyboardEvent: IKeyboardEvent) {
383-
switch (keyboardEvent.keyCode) {
384-
case KeyCode.Enter:
385-
this.submitSearch();
386-
return;
387-
default:
388-
return;
423+
private onReplaceInputKeyDown(keyboardEvent: IKeyboardEvent) {
424+
if (keyboardEvent.equals(KeyCode.Enter)) {
425+
this.submitSearch();
426+
keyboardEvent.preventDefault();
427+
}
428+
429+
else if (keyboardEvent.equals(KeyCode.Tab)) {
430+
this.searchInput.focusOnCaseSensitive();
431+
keyboardEvent.preventDefault();
432+
}
433+
434+
else if (keyboardEvent.equals(KeyMod.Shift | KeyCode.Tab)) {
435+
this.searchInput.focus();
436+
keyboardEvent.preventDefault();
389437
}
390438
}
391439

src/vs/workbench/parts/search/electron-browser/search.contribution.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ import { getMultiSelectedResources } from 'vs/workbench/parts/files/browser/file
5252
import { Schemas } from 'vs/base/common/network';
5353
import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
5454
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
55-
import { openSearchView, getSearchView, ReplaceAllInFolderAction, ReplaceAllAction, CloseReplaceAction, FocusNextInputAction, FocusPreviousInputAction, FocusNextSearchResultAction, FocusPreviousSearchResultAction, ReplaceInFilesAction, FindInFilesAction, FocusActiveEditorCommand, toggleCaseSensitiveCommand, ShowNextSearchTermAction, ShowPreviousSearchTermAction, toggleRegexCommand, ShowPreviousSearchIncludeAction, ShowNextSearchIncludeAction, CollapseDeepestExpandedLevelAction, toggleWholeWordCommand, RemoveAction, ReplaceAction, ClearSearchResultsAction, copyPathCommand, copyMatchCommand, copyAllCommand, ShowNextSearchExcludeAction, ShowPreviousSearchExcludeAction, clearHistoryCommand, ShowNextReplaceTermAction, ShowPreviousReplaceTermAction } from 'vs/workbench/parts/search/browser/searchActions';
55+
import { openSearchView, getSearchView, ReplaceAllInFolderAction, ReplaceAllAction, CloseReplaceAction, FocusNextSearchResultAction, FocusPreviousSearchResultAction, ReplaceInFilesAction, FindInFilesAction, FocusActiveEditorCommand, toggleCaseSensitiveCommand, ShowNextSearchTermAction, ShowPreviousSearchTermAction, toggleRegexCommand, ShowPreviousSearchIncludeAction, ShowNextSearchIncludeAction, CollapseDeepestExpandedLevelAction, toggleWholeWordCommand, RemoveAction, ReplaceAction, ClearSearchResultsAction, copyPathCommand, copyMatchCommand, copyAllCommand, ShowNextSearchExcludeAction, ShowPreviousSearchExcludeAction, clearHistoryCommand, ShowNextReplaceTermAction, ShowPreviousReplaceTermAction } from 'vs/workbench/parts/search/browser/searchActions';
5656
import { VIEW_ID, ISearchConfigurationProperties } from 'vs/platform/search/common/search';
5757
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
5858
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
@@ -179,26 +179,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
179179
}
180180
});
181181

182-
KeybindingsRegistry.registerCommandAndKeybindingRule({
183-
id: FocusNextInputAction.ID,
184-
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
185-
when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.InputBoxFocusedKey),
186-
primary: KeyCode.DownArrow,
187-
handler: (accessor, args: any) => {
188-
accessor.get(IInstantiationService).createInstance(FocusNextInputAction, FocusNextInputAction.ID, '').run();
189-
}
190-
});
191-
192-
KeybindingsRegistry.registerCommandAndKeybindingRule({
193-
id: FocusPreviousInputAction.ID,
194-
weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
195-
when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.InputBoxFocusedKey, Constants.SearchInputBoxFocusedKey.toNegated()),
196-
primary: KeyCode.UpArrow,
197-
handler: (accessor, args: any) => {
198-
accessor.get(IInstantiationService).createInstance(FocusPreviousInputAction, FocusPreviousInputAction.ID, '').run();
199-
}
200-
});
201-
202182
MenuRegistry.appendMenuItem(MenuId.SearchContext, {
203183
command: {
204184
id: Constants.ReplaceActionId,

0 commit comments

Comments
 (0)