diff --git a/src/plugins/console/public/application/components/help_panel.tsx b/src/plugins/console/public/application/components/help_panel.tsx index 50238006f6ce2..ed57d3178df4b 100644 --- a/src/plugins/console/public/application/components/help_panel.tsx +++ b/src/plugins/console/public/application/components/help_panel.tsx @@ -140,6 +140,13 @@ export function HelpPanel(props: Props) { defaultMessage="Select the currently selected or the top most term in auto-complete menu" /> +
Ctrl/Cmd + L
+
+ +
Esc
void; refreshAutocompleteSettings: (selectedSettings: DevToolsSettings['autocomplete']) => void; settings: DevToolsSettings; + editorInstance: SenseEditor | null; } export function DevToolsSettingsModal(props: Props) { @@ -74,7 +77,10 @@ export function DevToolsSettingsModal(props: Props) { const [polling, setPolling] = useState(props.settings.polling); const [pollInterval, setPollInterval] = useState(props.settings.pollInterval); const [tripleQuotes, setTripleQuotes] = useState(props.settings.tripleQuotes); - const [historyDisabled, setHistoryDisabled] = useState(props.settings.historyDisabled); + const [isHistoryDisabled, setIsHistoryDisabled] = useState(props.settings.isHistoryDisabled); + const [isKeyboardShortcutsDisabled, setIsKeyboardShortcutsDisabled] = useState( + props.settings.isKeyboardShortcutsDisabled + ); const autoCompleteCheckboxes = [ { @@ -134,7 +140,8 @@ export function DevToolsSettingsModal(props: Props) { polling, pollInterval, tripleQuotes, - historyDisabled, + isHistoryDisabled, + isKeyboardShortcutsDisabled, }); } @@ -145,6 +152,21 @@ export function DevToolsSettingsModal(props: Props) { setPollInterval(sanitizedValue); }, []); + const toggleKeyboardShortcuts = useCallback( + (isDisabled: boolean) => { + if (props.editorInstance) { + unregisterCommands(props.editorInstance); + setIsKeyboardShortcutsDisabled(isDisabled); + } + }, + [props.editorInstance] + ); + + const toggleSavingToHistory = useCallback( + (isDisabled: boolean) => setIsHistoryDisabled(isDisabled), + [] + ); + // It only makes sense to show polling options if the user needs to fetch any data. const pollingFields = fields || indices || templates ? ( @@ -160,7 +182,7 @@ export function DevToolsSettingsModal(props: Props) { } > @@ -267,15 +289,34 @@ export function DevToolsSettingsModal(props: Props) { } > } - onChange={(e) => setHistoryDisabled(e.target.checked)} + onChange={(e) => toggleSavingToHistory(e.target.checked)} + /> + + + + } + > + + } + onChange={(e) => toggleKeyboardShortcuts(e.target.checked)} /> diff --git a/src/plugins/console/public/application/containers/editor/editor.tsx b/src/plugins/console/public/application/containers/editor/editor.tsx index df017250664e4..4931648c07df7 100644 --- a/src/plugins/console/public/application/containers/editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/editor.tsx @@ -15,15 +15,17 @@ import { Panel, PanelsContainer } from '../../containers'; import { Editor as EditorUI, EditorOutput } from './legacy/console_editor'; import { StorageKeys } from '../../../services'; import { useEditorReadContext, useServicesContext, useRequestReadContext } from '../../contexts'; +import type { SenseEditor } from '../../models'; const INITIAL_PANEL_WIDTH = 50; const PANEL_MIN_WIDTH = '100px'; interface Props { loading: boolean; + setEditorInstance: (instance: SenseEditor) => void; } -export const Editor = memo(({ loading }: Props) => { +export const Editor = memo(({ loading, setEditorInstance }: Props) => { const { services: { storage }, } = useServicesContext(); @@ -61,7 +63,10 @@ export const Editor = memo(({ loading }: Props) => { {loading ? ( ) : ( - + )} { - + {}} /> diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx index 9105d86f9e2ec..9a879b4a72916 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -34,11 +34,13 @@ import { autoIndent, getDocumentation } from '../console_menu_actions'; import { subscribeResizeChecker } from '../subscribe_console_resize_checker'; import { applyCurrentSettings } from './apply_editor_settings'; import { registerCommands } from './keyboard_shortcuts'; +import type { SenseEditor } from '../../../../models/sense_editor'; const { useUIAceKeyboardMode } = ace; export interface EditorProps { initialTextValue: string; + setEditorInstance: (instance: SenseEditor) => void; } interface QueryParams { @@ -62,7 +64,7 @@ const DEFAULT_INPUT_VALUE = `GET _search const inputId = 'ConAppInputTextarea'; -function EditorUI({ initialTextValue }: EditorProps) { +function EditorUI({ initialTextValue, setEditorInstance }: EditorProps) { const { services: { history, notifications, settings: settingsService, esHostService, http }, docLinkVersion, @@ -225,12 +227,22 @@ function EditorUI({ initialTextValue }: EditorProps) { }, [settings]); useEffect(() => { - registerCommands({ - senseEditor: editorInstanceRef.current!, - sendCurrentRequestToES, - openDocumentation, - }); - }, [sendCurrentRequestToES, openDocumentation]); + const { isKeyboardShortcutsDisabled } = settings; + if (!isKeyboardShortcutsDisabled) { + registerCommands({ + senseEditor: editorInstanceRef.current!, + sendCurrentRequestToES, + openDocumentation, + }); + } + }, [sendCurrentRequestToES, openDocumentation, settings]); + + useEffect(() => { + const { current: editor } = editorInstanceRef; + if (editor) { + setEditorInstance(editor); + } + }, [setEditorInstance]); return (
diff --git a/src/plugins/console/public/application/containers/editor/legacy/console_editor/keyboard_shortcuts.ts b/src/plugins/console/public/application/containers/editor/legacy/console_editor/keyboard_shortcuts.ts index 4f09a49f3ac96..3d100ef0a5528 100644 --- a/src/plugins/console/public/application/containers/editor/legacy/console_editor/keyboard_shortcuts.ts +++ b/src/plugins/console/public/application/containers/editor/legacy/console_editor/keyboard_shortcuts.ts @@ -15,6 +15,15 @@ interface Actions { openDocumentation: () => void; } +const COMMANDS = { + SEND_TO_ELASTICSEARCH: 'send to Elasticsearch', + OPEN_DOCUMENTATION: 'open documentation', + AUTO_INDENT_REQUEST: 'auto indent request', + MOVE_TO_PREVIOUS_REQUEST: 'move to previous request start or end', + MOVE_TO_NEXT_REQUEST: 'move to next request start or end', + GO_TO_LINE: 'gotoline', +}; + export function registerCommands({ senseEditor, sendCurrentRequestToES, @@ -28,12 +37,14 @@ export function registerCommands({ coreEditor.registerKeyboardShortcut({ keys: { win: 'Ctrl-Enter', mac: 'Command-Enter' }, - name: 'send to Elasticsearch', - fn: () => sendCurrentRequestToES(), + name: COMMANDS.SEND_TO_ELASTICSEARCH, + fn: () => { + sendCurrentRequestToES(); + }, }); coreEditor.registerKeyboardShortcut({ - name: 'open documentation', + name: COMMANDS.OPEN_DOCUMENTATION, keys: { win: 'Ctrl-/', mac: 'Command-/' }, fn: () => { openDocumentation(); @@ -41,7 +52,7 @@ export function registerCommands({ }); coreEditor.registerKeyboardShortcut({ - name: 'auto indent request', + name: COMMANDS.AUTO_INDENT_REQUEST, keys: { win: 'Ctrl-I', mac: 'Command-I' }, fn: () => { throttledAutoIndent(); @@ -49,7 +60,7 @@ export function registerCommands({ }); coreEditor.registerKeyboardShortcut({ - name: 'move to previous request start or end', + name: COMMANDS.MOVE_TO_PREVIOUS_REQUEST, keys: { win: 'Ctrl-Up', mac: 'Command-Up' }, fn: () => { senseEditor.moveToPreviousRequestEdge(); @@ -57,10 +68,28 @@ export function registerCommands({ }); coreEditor.registerKeyboardShortcut({ - name: 'move to next request start or end', + name: COMMANDS.MOVE_TO_NEXT_REQUEST, keys: { win: 'Ctrl-Down', mac: 'Command-Down' }, fn: () => { senseEditor.moveToNextRequestEdge(false); }, }); + + coreEditor.registerKeyboardShortcut({ + name: COMMANDS.GO_TO_LINE, + keys: { win: 'Ctrl-L', mac: 'Command-L' }, + fn: (editor) => { + const line = parseInt(prompt('Enter line number') ?? '', 10); + if (!isNaN(line)) { + editor.gotoLine(line); + } + }, + }); +} + +export function unregisterCommands(senseEditor: SenseEditor) { + const coreEditor = senseEditor.getCoreEditor(); + Object.values(COMMANDS).forEach((command) => { + coreEditor.unregisterKeyboardShortcut(command); + }); } diff --git a/src/plugins/console/public/application/containers/main/main.tsx b/src/plugins/console/public/application/containers/main/main.tsx index 30bf23d94a327..5895b919f9842 100644 --- a/src/plugins/console/public/application/containers/main/main.tsx +++ b/src/plugins/console/public/application/containers/main/main.tsx @@ -25,6 +25,7 @@ import { useServicesContext, useEditorReadContext, useRequestReadContext } from import { useDataInit } from '../../hooks'; import { getTopNavConfig } from './get_top_nav'; +import type { SenseEditor } from '../../models/sense_editor'; export function Main() { const { @@ -46,6 +47,8 @@ export function Main() { const [showSettings, setShowSettings] = useState(false); const [showHelp, setShowHelp] = useState(false); + const [editorInstance, setEditorInstance] = useState(null); + const renderConsoleHistory = () => { return editorsReady ? setShowHistory(false)} /> : null; }; @@ -108,7 +111,7 @@ export function Main() { {showingHistory ? {renderConsoleHistory()} : null} - + @@ -121,7 +124,9 @@ export function Main() { /> ) : null} - {showSettings ? setShowSettings(false)} /> : null} + {showSettings ? ( + setShowSettings(false)} editorInstance={editorInstance} /> + ) : null} {showHelp ? setShowHelp(false)} /> : null}
diff --git a/src/plugins/console/public/application/containers/settings.tsx b/src/plugins/console/public/application/containers/settings.tsx index f0ec64f4b13b2..c0bd1b18fff26 100644 --- a/src/plugins/console/public/application/containers/settings.tsx +++ b/src/plugins/console/public/application/containers/settings.tsx @@ -15,6 +15,7 @@ import { AutocompleteOptions, DevToolsSettingsModal } from '../components'; import { retrieveAutoCompleteInfo } from '../../lib/mappings/mappings'; import { useServicesContext, useEditorActionContext } from '../contexts'; import { DevToolsSettings, Settings as SettingsService } from '../../services'; +import type { SenseEditor } from '../models'; const getAutocompleteDiff = ( newSettings: DevToolsSettings, @@ -70,9 +71,10 @@ const fetchAutocompleteSettingsIfNeeded = ( export interface Props { onClose: () => void; + editorInstance: SenseEditor | null; } -export function Settings({ onClose }: Props) { +export function Settings({ onClose, editorInstance }: Props) { const { services: { settings, http }, } = useServicesContext(); @@ -102,6 +104,7 @@ export function Settings({ onClose }: Props) { refreshAutocompleteSettings(http, settings, selectedSettings) } settings={settings.toJSON()} + editorInstance={editorInstance} /> ); } diff --git a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts index f53a9dadbe108..e7c436c9806b3 100644 --- a/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts +++ b/src/plugins/console/public/application/hooks/use_send_current_request_to_es/use_send_current_request_to_es.ts @@ -49,9 +49,9 @@ export const useSendCurrentRequestToES = () => { const results = await sendRequestToES({ http, requests }); let saveToHistoryError: undefined | Error; - const { historyDisabled } = settings.toJSON(); + const { isHistoryDisabled } = settings.toJSON(); - if (!historyDisabled) { + if (!isHistoryDisabled) { results.forEach(({ request: { path, method, data } }) => { try { history.addToHistory(path, method, data); @@ -81,7 +81,7 @@ export const useSendCurrentRequestToES = () => { notifications.toasts.remove(toast); }, onDisableSavingToHistory: () => { - settings.setHistoryDisabled(true); + settings.setIsHistoryDisabled(true); notifications.toasts.remove(toast); }, }), diff --git a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts index 7a90dbe138f17..f13597e933bb2 100644 --- a/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts +++ b/src/plugins/console/public/application/models/legacy_core_editor/legacy_core_editor.ts @@ -297,6 +297,11 @@ export class LegacyCoreEditor implements CoreEditor { }); } + unregisterKeyboardShortcut(command: string) { + // @ts-ignore + this.editor.commands.removeCommand(command); + } + legacyUpdateUI(range: Range) { if (!this.$actions) { return; diff --git a/src/plugins/console/public/services/settings.ts b/src/plugins/console/public/services/settings.ts index 1a7eff3e7ca54..6757631a7d7c5 100644 --- a/src/plugins/console/public/services/settings.ts +++ b/src/plugins/console/public/services/settings.ts @@ -15,7 +15,8 @@ export const DEFAULT_SETTINGS = Object.freeze({ tripleQuotes: true, wrapMode: true, autocomplete: Object.freeze({ fields: true, indices: true, templates: true, dataStreams: true }), - historyDisabled: false, + isHistoryDisabled: false, + isKeyboardShortcutsDisabled: false, }); export interface DevToolsSettings { @@ -30,72 +31,96 @@ export interface DevToolsSettings { polling: boolean; pollInterval: number; tripleQuotes: boolean; - historyDisabled: boolean; + isHistoryDisabled: boolean; + isKeyboardShortcutsDisabled: boolean; +} + +enum SettingKeys { + FONT_SIZE = 'font_size', + WRAP_MODE = 'wrap_mode', + TRIPLE_QUOTES = 'triple_quotes', + AUTOCOMPLETE_SETTINGS = 'autocomplete_settings', + CONSOLE_POLLING = 'console_polling', + POLL_INTERVAL = 'poll_interval', + IS_HISTORY_DISABLED = 'is_history_disabled', + IS_KEYBOARD_SHORTCUTS_DISABLED = 'is_keyboard_shortcuts_disabled', } export class Settings { constructor(private readonly storage: Storage) {} getFontSize() { - return this.storage.get('font_size', DEFAULT_SETTINGS.fontSize); + return this.storage.get(SettingKeys.FONT_SIZE, DEFAULT_SETTINGS.fontSize); } setFontSize(size: number) { - this.storage.set('font_size', size); + this.storage.set(SettingKeys.FONT_SIZE, size); return true; } getWrapMode() { - return this.storage.get('wrap_mode', DEFAULT_SETTINGS.wrapMode); + return this.storage.get(SettingKeys.WRAP_MODE, DEFAULT_SETTINGS.wrapMode); } setWrapMode(mode: boolean) { - this.storage.set('wrap_mode', mode); + this.storage.set(SettingKeys.WRAP_MODE, mode); return true; } setTripleQuotes(tripleQuotes: boolean) { - this.storage.set('triple_quotes', tripleQuotes); + this.storage.set(SettingKeys.TRIPLE_QUOTES, tripleQuotes); return true; } getTripleQuotes() { - return this.storage.get('triple_quotes', DEFAULT_SETTINGS.tripleQuotes); + return this.storage.get(SettingKeys.TRIPLE_QUOTES, DEFAULT_SETTINGS.tripleQuotes); } getAutocomplete() { - return this.storage.get('autocomplete_settings', DEFAULT_SETTINGS.autocomplete); + return this.storage.get(SettingKeys.AUTOCOMPLETE_SETTINGS, DEFAULT_SETTINGS.autocomplete); } setAutocomplete(settings: object) { - this.storage.set('autocomplete_settings', settings); + this.storage.set(SettingKeys.AUTOCOMPLETE_SETTINGS, settings); return true; } getPolling() { - return this.storage.get('console_polling', DEFAULT_SETTINGS.polling); + return this.storage.get(SettingKeys.CONSOLE_POLLING, DEFAULT_SETTINGS.polling); } setPolling(polling: boolean) { - this.storage.set('console_polling', polling); + this.storage.set(SettingKeys.CONSOLE_POLLING, polling); return true; } - setHistoryDisabled(disable: boolean) { - this.storage.set('disable_history', disable); + setIsHistoryDisabled(isDisabled: boolean) { + this.storage.set(SettingKeys.IS_HISTORY_DISABLED, isDisabled); return true; } - getHistoryDisabled() { - return this.storage.get('disable_history', DEFAULT_SETTINGS.historyDisabled); + getIsHistoryDisabled() { + return this.storage.get(SettingKeys.IS_HISTORY_DISABLED, DEFAULT_SETTINGS.isHistoryDisabled); } setPollInterval(interval: number) { - this.storage.set('poll_interval', interval); + this.storage.set(SettingKeys.POLL_INTERVAL, interval); } getPollInterval() { - return this.storage.get('poll_interval', DEFAULT_SETTINGS.pollInterval); + return this.storage.get(SettingKeys.POLL_INTERVAL, DEFAULT_SETTINGS.pollInterval); + } + + setIsKeyboardShortcutsDisabled(disable: boolean) { + this.storage.set(SettingKeys.IS_KEYBOARD_SHORTCUTS_DISABLED, disable); + return true; + } + + getIsKeyboardShortcutsDisabled() { + return this.storage.get( + SettingKeys.IS_KEYBOARD_SHORTCUTS_DISABLED, + DEFAULT_SETTINGS.isKeyboardShortcutsDisabled + ); } toJSON(): DevToolsSettings { @@ -106,7 +131,8 @@ export class Settings { fontSize: parseFloat(this.getFontSize()), polling: Boolean(this.getPolling()), pollInterval: this.getPollInterval(), - historyDisabled: Boolean(this.getHistoryDisabled()), + isHistoryDisabled: Boolean(this.getIsHistoryDisabled()), + isKeyboardShortcutsDisabled: Boolean(this.getIsKeyboardShortcutsDisabled()), }; } @@ -117,7 +143,8 @@ export class Settings { autocomplete, polling, pollInterval, - historyDisabled, + isHistoryDisabled, + isKeyboardShortcutsDisabled, }: DevToolsSettings) { this.setFontSize(fontSize); this.setWrapMode(wrapMode); @@ -125,7 +152,8 @@ export class Settings { this.setAutocomplete(autocomplete); this.setPolling(polling); this.setPollInterval(pollInterval); - this.setHistoryDisabled(historyDisabled); + this.setIsHistoryDisabled(isHistoryDisabled); + this.setIsKeyboardShortcutsDisabled(isKeyboardShortcutsDisabled); } } diff --git a/src/plugins/console/public/types/core_editor.ts b/src/plugins/console/public/types/core_editor.ts index cc344d6bcc881..db8010afe762b 100644 --- a/src/plugins/console/public/types/core_editor.ts +++ b/src/plugins/console/public/types/core_editor.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import type { Editor } from 'brace'; import { TokensProvider } from './tokens_provider'; import { Token } from './token'; @@ -252,10 +253,15 @@ export interface CoreEditor { */ registerKeyboardShortcut(opts: { keys: string | { win?: string; mac?: string }; - fn: () => void; + fn: (editor: Editor) => void; name: string; }): void; + /** + * Unregister a keyboard shortcut and provide a command name + */ + unregisterKeyboardShortcut(command: string): void; + /** * Register a completions function that will be called when the editor * detects a change