From 13fb9fdfd24254281841db71f8cb3b2fd125304e Mon Sep 17 00:00:00 2001 From: Abbie Petchtes Date: Fri, 13 Apr 2018 11:11:25 -0700 Subject: [PATCH] Add keyboard shortcuts for focus on current query and go to next output query tab (#1153) * add go to next output query tab shotcut * add a new keyboard shortcut for focus on current query * minor change --- .../base/browser/ui/panel/panel.component.ts | 14 +++++++++- .../parts/grid/common/gridContentEvents.ts | 1 + src/sql/parts/grid/views/gridActions.ts | 2 ++ src/sql/parts/grid/views/gridCommands.ts | 4 +++ .../parts/grid/views/gridParentComponent.ts | 9 ++++++ .../parts/grid/views/query/query.component.ts | 5 ++++ .../parts/query/common/query.contribution.ts | 20 ++++++++++++- .../query/execution/keyboardQueryActions.ts | 28 +++++++++++++++++++ .../query/views/queryOutput.component.ts | 5 ++++ 9 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/sql/base/browser/ui/panel/panel.component.ts b/src/sql/base/browser/ui/panel/panel.component.ts index 21e74bf4c7..1da36104c6 100644 --- a/src/sql/base/browser/ui/panel/panel.component.ts +++ b/src/sql/base/browser/ui/panel/panel.component.ts @@ -182,7 +182,7 @@ export class PanelComponent extends Disposable implements AfterContentInit, OnIn if (input instanceof TabComponent) { tab = input; } else if (types.isNumber(input)) { - tab = this._tabs[input]; + tab = this._tabs.toArray()[input]; } else if (types.isString(input)) { tab = this._tabs.find(i => i.identifier === input); } @@ -226,6 +226,18 @@ export class PanelComponent extends Disposable implements AfterContentInit, OnIn return this._activeTab.identifier; } + /** + * Select on the next tab + */ + public selectOnNextTab(): void { + let activeIndex = this._tabs.toArray().findIndex(i => i === this._activeTab); + let nextTabIndex = activeIndex + 1; + if (nextTabIndex === this._tabs.length) { + nextTabIndex = 0; + } + this.selectTab(nextTabIndex); + } + private findAndRemoveTabFromMRU(tab: TabComponent): void { let mruIndex = this._mru.findIndex(i => i === tab); diff --git a/src/sql/parts/grid/common/gridContentEvents.ts b/src/sql/parts/grid/common/gridContentEvents.ts index 9ad8e5bfad..91609e06b8 100644 --- a/src/sql/parts/grid/common/gridContentEvents.ts +++ b/src/sql/parts/grid/common/gridContentEvents.ts @@ -17,3 +17,4 @@ export let SelectAllMessages = 'SelectAllMessages'; export let SaveAsCsv = 'SaveAsCSV'; export let SaveAsJSON = 'SaveAsJSON'; export let SaveAsExcel = 'SaveAsExcel'; +export let GoToNextQueryOutputTab = 'GoToNextQueryOutputTab'; diff --git a/src/sql/parts/grid/views/gridActions.ts b/src/sql/parts/grid/views/gridActions.ts index b20c37ac1d..bd773b0a1e 100644 --- a/src/sql/parts/grid/views/gridActions.ts +++ b/src/sql/parts/grid/views/gridActions.ts @@ -24,6 +24,8 @@ export const MESSAGES_SELECTALL_ID = 'grid.messages.selectAll'; export const MESSAGES_COPY_ID = 'grid.messages.copy'; export const TOGGLERESULTS_ID = 'grid.toggleResultPane'; export const TOGGLEMESSAGES_ID = 'grid.toggleMessagePane'; +export const GOTONEXTQUERYOUTPUTTAB_ID = 'query.goToNextQueryOutputTab'; + export class GridActionProvider { diff --git a/src/sql/parts/grid/views/gridCommands.ts b/src/sql/parts/grid/views/gridCommands.ts index 18a6a2d3f2..f53f3ee53a 100644 --- a/src/sql/parts/grid/views/gridCommands.ts +++ b/src/sql/parts/grid/views/gridCommands.ts @@ -53,6 +53,10 @@ export const toggleResultsPane = (accessor: ServicesAccessor) => { runActionOnActiveResultsEditor(accessor, GridContentEvents.ToggleResultPane); }; +export const goToNextQueryOutputTab = (accessor: ServicesAccessor) => { + runActionOnActiveResultsEditor(accessor, GridContentEvents.GoToNextQueryOutputTab); +}; + export const saveAsCsv = (accessor: ServicesAccessor) => { runActionOnActiveResultsEditor(accessor, GridContentEvents.SaveAsCsv); }; diff --git a/src/sql/parts/grid/views/gridParentComponent.ts b/src/sql/parts/grid/views/gridParentComponent.ts index 190a72fa97..494602aac2 100644 --- a/src/sql/parts/grid/views/gridParentComponent.ts +++ b/src/sql/parts/grid/views/gridParentComponent.ts @@ -165,6 +165,9 @@ export abstract class GridParentComponent { case GridContentEvents.SaveAsExcel: self.sendSaveRequest(SaveFormat.EXCEL); break; + case GridContentEvents.GoToNextQueryOutputTab: + self.goToNextQueryOutputTab(); + break; default: error('Unexpected grid content event type "' + type + '" sent'); break; @@ -272,6 +275,9 @@ export abstract class GridParentComponent { return ''; } + protected goToNextQueryOutputTab(): void { + } + private initShortcutsBase(): void { let shortcuts = { 'ToggleResultPane': () => { @@ -297,6 +303,9 @@ export abstract class GridParentComponent { }, 'SaveAsExcel': () => { this.sendSaveRequest(SaveFormat.EXCEL); + }, + 'GoToNextQueryOutputTab': () => { + this.goToNextQueryOutputTab(); } }; diff --git a/src/sql/parts/grid/views/query/query.component.ts b/src/sql/parts/grid/views/query/query.component.ts index 9de1302f6a..25838cef12 100644 --- a/src/sql/parts/grid/views/query/query.component.ts +++ b/src/sql/parts/grid/views/query/query.component.ts @@ -146,6 +146,7 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes public queryExecutionStatus: EventEmitter = new EventEmitter(); public queryPlanAvailable: EventEmitter = new EventEmitter(); public showChartRequested: EventEmitter = new EventEmitter(); + public goToNextQueryOutputTabRequested: EventEmitter = new EventEmitter(); @Input() public queryParameters: QueryComponentParams; @@ -578,6 +579,10 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes } } + protected goToNextQueryOutputTab(): void { + this.goToNextQueryOutputTabRequested.emit(); + } + protected toggleResultPane(): void { this.resultActive = !this.resultActive; this._cd.detectChanges(); diff --git a/src/sql/parts/query/common/query.contribution.ts b/src/sql/parts/query/common/query.contribution.ts index f483f87c9a..9a50a38fdd 100644 --- a/src/sql/parts/query/common/query.contribution.ts +++ b/src/sql/parts/query/common/query.contribution.ts @@ -25,7 +25,7 @@ import { EditDataEditor } from 'sql/parts/editData/editor/editDataEditor'; import { EditDataInput } from 'sql/parts/editData/common/editDataInput'; import { RunQueryKeyboardAction, RunCurrentQueryKeyboardAction, CancelQueryKeyboardAction, RefreshIntellisenseKeyboardAction, ToggleQueryResultsKeyboardAction, - RunQueryShortcutAction, RunCurrentQueryWithActualPlanKeyboardAction + RunQueryShortcutAction, RunCurrentQueryWithActualPlanKeyboardAction, FocusOnCurrentQueryKeyboardAction } from 'sql/parts/query/execution/keyboardQueryActions'; import * as gridActions from 'sql/parts/grid/views/gridActions'; import * as gridCommands from 'sql/parts/grid/views/gridCommands'; @@ -132,6 +132,16 @@ actionRegistry.registerWorkbenchAction( RefreshIntellisenseKeyboardAction.LABEL ); +actionRegistry.registerWorkbenchAction( + new SyncActionDescriptor( + FocusOnCurrentQueryKeyboardAction, + FocusOnCurrentQueryKeyboardAction.ID, + FocusOnCurrentQueryKeyboardAction.LABEL, + { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_Q } + ), + FocusOnCurrentQueryKeyboardAction.LABEL +); + // Grid actions actionRegistry.registerWorkbenchAction( @@ -217,6 +227,14 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ handler: gridCommands.toggleMessagePane }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: gridActions.GOTONEXTQUERYOUTPUTTAB_ID, + weight: KeybindingsRegistry.WEIGHT.workbenchContrib(gridCommandsWeightBonus), + when: QueryEditorVisibleCondition, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_P, + handler: gridCommands.goToNextQueryOutputTab +}); + // Intellisense and other configuration options let registryProperties = { 'sql.messagesDefaultOpen': { diff --git a/src/sql/parts/query/execution/keyboardQueryActions.ts b/src/sql/parts/query/execution/keyboardQueryActions.ts index bfaa61d6d8..3471f1ebef 100644 --- a/src/sql/parts/query/execution/keyboardQueryActions.ts +++ b/src/sql/parts/query/execution/keyboardQueryActions.ts @@ -51,6 +51,34 @@ function escapeSqlString(input: string, escapeChar: string) { return output; } + +/** + * Locates the active editor and call focus() on the editor if it is a QueryEditor. + */ +export class FocusOnCurrentQueryKeyboardAction extends Action { + + public static ID = 'focusOnCurrentQueryKeyboardAction'; + public static LABEL = nls.localize('focusOnCurrentQueryKeyboardAction', 'Focus on Current Query'); + + constructor( + id: string, + label: string, + @IWorkbenchEditorService private _editorService: IWorkbenchEditorService + ) { + super(id, label); + this.enabled = true; + } + + public run(): TPromise { + let editor = this._editorService.getActiveEditor(); + if (editor && editor instanceof QueryEditor) { + let queryEditor: QueryEditor = editor; + queryEditor.focus(); + } + return TPromise.as(null); + } +} + /** * Locates the active editor and calls runQuery() on the editor if it is a QueryEditor. */ diff --git a/src/sql/parts/query/views/queryOutput.component.ts b/src/sql/parts/query/views/queryOutput.component.ts index 0172a669a7..08434eead4 100644 --- a/src/sql/parts/query/views/queryOutput.component.ts +++ b/src/sql/parts/query/views/queryOutput.component.ts @@ -102,6 +102,11 @@ export class QueryOutputComponent implements OnInit, OnDestroy { this._cd.detectChanges(); } }))); + + this._disposables.push(toDisposableSubscription(this.queryComponent.goToNextQueryOutputTabRequested.subscribe(() => { + let activeTab = this._panel.getActiveTab; + this._panel.selectOnNextTab(); + }))); } public ngOnDestroy(): void {