mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-23 07:38:58 -04:00
Add a keyboard shortcut for beautify request body (#2733)
This commit is contained in:
@@ -129,6 +129,7 @@ export const hotKeyRefs: Record<string, HotKeyDefinition> = {
|
||||
CLOSE_DROPDOWN: defineHotKey('closeDropdown', 'Close Dropdown'),
|
||||
CLOSE_MODAL: defineHotKey('closeModal', 'Close Modal'),
|
||||
ENVIRONMENT_UNCOVER_VARIABLES: defineHotKey('environment_uncoverVariables', 'Uncover Variables'),
|
||||
BEAUTIFY_REQUEST_BODY: defineHotKey('beautifyRequestBody', 'Beautify Active Code Editors'),
|
||||
GRAPHQL_EXPLORER_FOCUS_FILTER: defineHotKey('graphql_explorer_focus_filter', 'Focus GraphQL Explorer Filter'),
|
||||
// Designer-specific
|
||||
SHOW_SPEC_EDITOR: defineHotKey('activity_specEditor', 'Show Spec Activity'),
|
||||
@@ -278,6 +279,10 @@ const defaultRegistry: HotKeyRegistry = {
|
||||
keyComb(false, false, true, true, keyboardKeys.f.keyCode),
|
||||
keyComb(true, false, true, false, keyboardKeys.f.keyCode),
|
||||
),
|
||||
[hotKeyRefs.BEAUTIFY_REQUEST_BODY.id]: keyBinds(
|
||||
keyComb(false, false, true, true, keyboardKeys.i.keyCode),
|
||||
keyComb(true, false, true, false, keyboardKeys.i.keyCode),
|
||||
),
|
||||
[hotKeyRefs.SHOW_SPEC_EDITOR.id]: keyBinds(
|
||||
keyComb(false, false, true, true, keyboardKeys.s.keyCode),
|
||||
keyComb(true, false, true, false, keyboardKeys.s.keyCode),
|
||||
|
||||
@@ -11,6 +11,7 @@ import { json as jsonPrettify } from 'insomnia-prettify';
|
||||
import { query as queryXPath } from 'insomnia-xpath';
|
||||
import jq from 'jsonpath';
|
||||
import React, { Component, CSSProperties, ReactNode } from 'react';
|
||||
import { unreachable } from 'ts-assert-unreachable';
|
||||
import vkBeautify from 'vkbeautify';
|
||||
import zprint from 'zprint-clj';
|
||||
|
||||
@@ -20,6 +21,8 @@ import {
|
||||
EDITOR_KEY_MAP_VIM,
|
||||
isMac,
|
||||
} from '../../../common/constants';
|
||||
import { hotKeyRefs } from '../../../common/hotkeys';
|
||||
import { executeHotKey } from '../../../common/hotkeys-listener';
|
||||
import { keyboardKeys as keyCodes } from '../../../common/keyboard-keys';
|
||||
import * as misc from '../../../common/misc';
|
||||
import { HandleGetRenderContext, HandleRender } from '../../../common/render';
|
||||
@@ -28,6 +31,7 @@ import { NunjucksParsedTag } from '../../../templating/utils';
|
||||
import Dropdown from '../base/dropdown/dropdown';
|
||||
import DropdownButton from '../base/dropdown/dropdown-button';
|
||||
import DropdownItem from '../base/dropdown/dropdown-item';
|
||||
import KeydownBinder from '../keydown-binder';
|
||||
import FilterHelpModal from '../modals/filter-help-modal';
|
||||
import { showModal } from '../modals/index';
|
||||
import { normalizeIrregularWhitespace } from './normalizeIrregularWhitespace';
|
||||
@@ -162,7 +166,6 @@ class CodeEditor extends Component<Props, State> {
|
||||
codeMirror?: CodeMirror.EditorFromTextArea;
|
||||
private _filterInput: HTMLInputElement;
|
||||
private _autocompleteDebounce: NodeJS.Timeout | null = null;
|
||||
private _ignoreNextChange: boolean;
|
||||
private _filterTimeout: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor(props: Props) {
|
||||
@@ -641,12 +644,14 @@ class CodeEditor extends Component<Props, State> {
|
||||
: new Array((this.codeMirror?.getOption?.('indentUnit') || 0) + 1).join(' ');
|
||||
}
|
||||
|
||||
_handleBeautify() {
|
||||
this._prettify(this.codeMirror?.getValue());
|
||||
}
|
||||
_prettify() {
|
||||
const canPrettify = this._canPrettify();
|
||||
if (!canPrettify) {
|
||||
return;
|
||||
}
|
||||
|
||||
_prettify(code?: string) {
|
||||
this._codemirrorSetValue(code, true);
|
||||
const code = this.codeMirror?.getValue();
|
||||
this._codemirrorSetValue(code, canPrettify);
|
||||
}
|
||||
|
||||
_prettifyJSON(code: string) {
|
||||
@@ -698,6 +703,10 @@ class CodeEditor extends Component<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
async _handleKeyDown(event: KeyboardEvent) {
|
||||
executeHotKey(event, hotKeyRefs.BEAUTIFY_REQUEST_BODY, this._prettify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets options on the CodeMirror editor while also sanitizing them
|
||||
*/
|
||||
@@ -1047,9 +1056,7 @@ class CodeEditor extends Component<Props, State> {
|
||||
* Wrapper function to add extra behaviour to our onChange event
|
||||
*/
|
||||
_codemirrorValueChanged() {
|
||||
// Don't trigger change event if we're ignoring changes
|
||||
if (this._ignoreNextChange || !this.props.onChange) {
|
||||
this._ignoreNextChange = false;
|
||||
if (!this.props.onChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1074,33 +1081,34 @@ class CodeEditor extends Component<Props, State> {
|
||||
* @param code the code to set in the editor
|
||||
* @param forcePrettify
|
||||
*/
|
||||
_codemirrorSetValue(code = '', forcePrettify = false) {
|
||||
_codemirrorSetValue(code?: string, forcePrettify?: boolean) {
|
||||
if (typeof code !== 'string') {
|
||||
console.warn('Code editor was passed non-string value', code);
|
||||
return;
|
||||
}
|
||||
|
||||
const { autoPrettify, mode } = this.props;
|
||||
this._originalCode = code;
|
||||
|
||||
// If we're setting initial value, don't trigger onChange because the
|
||||
// user hasn't done anything yet
|
||||
if (!forcePrettify) {
|
||||
this._ignoreNextChange = true;
|
||||
}
|
||||
|
||||
const shouldPrettify = forcePrettify || this.props.autoPrettify;
|
||||
const shouldPrettify = forcePrettify || autoPrettify;
|
||||
|
||||
if (shouldPrettify && this._canPrettify()) {
|
||||
if (CodeEditor._isXML(this.props.mode)) {
|
||||
if (CodeEditor._isXML(mode)) {
|
||||
code = this._prettifyXML(code);
|
||||
} else if (CodeEditor._isEDN(this.props.mode)) {
|
||||
} else if (CodeEditor._isEDN(mode)) {
|
||||
code = CodeEditor._prettifyEDN(code);
|
||||
} else {
|
||||
} else if (CodeEditor._isJSON(mode)) {
|
||||
code = this._prettifyJSON(code);
|
||||
} else {
|
||||
unreachable('attempted to prettify in a mode that should not support prettifying');
|
||||
}
|
||||
}
|
||||
|
||||
this.codeMirror?.setValue(code);
|
||||
// this prevents codeMirror from needlessly setting the same thing repeatedly (which has the effect of moving the user's cursor and resetting the viewport scroll: a bad user experience)
|
||||
const currentCode = this.codeMirror?.getValue();
|
||||
if (currentCode === code) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.codeMirror?.setValue(code || '');
|
||||
}
|
||||
|
||||
_handleFilterHistorySelect(filter = '') {
|
||||
@@ -1219,7 +1227,7 @@ class CodeEditor extends Component<Props, State> {
|
||||
key="prettify"
|
||||
className="btn btn--compact"
|
||||
title="Auto-format request body whitespace"
|
||||
onClick={this._handleBeautify}
|
||||
onClick={this._prettify}
|
||||
>
|
||||
Beautify {contentTypeName}
|
||||
</button>,
|
||||
@@ -1244,6 +1252,7 @@ class CodeEditor extends Component<Props, State> {
|
||||
|
||||
return (
|
||||
<div className={classes} style={style} data-editor-type={type}>
|
||||
<KeydownBinder onKeydown={this._handleKeyDown} />
|
||||
<div
|
||||
className={classnames('editor__container', 'input', className)}
|
||||
style={styles}
|
||||
@@ -1259,8 +1268,7 @@ class CodeEditor extends Component<Props, State> {
|
||||
}}
|
||||
readOnly={readOnly}
|
||||
autoComplete="off"
|
||||
// NOTE: When setting this to empty string, it breaks the _ignoreNextChange logic on initial component mount
|
||||
defaultValue=" "
|
||||
defaultValue=""
|
||||
/>
|
||||
</div>
|
||||
{toolbar}
|
||||
|
||||
@@ -1,33 +1,53 @@
|
||||
import 'codemirror/addon/mode/simple';
|
||||
|
||||
import CodeMirror from 'codemirror';
|
||||
|
||||
/** regular key-value header tokens */
|
||||
const keyValueHeaders = [
|
||||
{
|
||||
regex: /^(> )([^:]*:)(.*)$/,
|
||||
token: ['curl-prefix curl-out', 'curl-out', 'curl-out curl-value'],
|
||||
},
|
||||
{
|
||||
regex: /^(< )([^:]*:)(.*)$/,
|
||||
token: ['curl-prefix curl-in', 'curl-in', 'curl-in curl-value'],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* @example POST /foo/bar HTTP/1.1
|
||||
*/
|
||||
const headerFields = [
|
||||
{
|
||||
regex: /^(> )([^:]+ .*)$/,
|
||||
token: ['curl-prefix curl-out curl-header', 'curl-out curl-header'],
|
||||
},
|
||||
{
|
||||
regex: /^(< )([^:]+ .*)$/,
|
||||
token: ['curl-prefix curl-in curl-header', 'curl-in curl-header'],
|
||||
},
|
||||
];
|
||||
|
||||
const data = [
|
||||
{
|
||||
regex: /^(\| )(.*)$/,
|
||||
token: ['curl-prefix curl-data', 'curl-data'],
|
||||
},
|
||||
];
|
||||
|
||||
const informationalText = [
|
||||
{
|
||||
regex: /^(\* )(.*)$/,
|
||||
token: ['curl-prefix curl-comment', 'curl-comment'],
|
||||
},
|
||||
];
|
||||
|
||||
CodeMirror.defineSimpleMode('curl', {
|
||||
start: [
|
||||
// Regular key-value header tokens
|
||||
{
|
||||
regex: /^(> )([^:]*:)(.*)$/,
|
||||
token: ['curl-prefix curl-out', 'curl-out', 'curl-out curl-value'],
|
||||
},
|
||||
{
|
||||
regex: /^(< )([^:]*:)(.*)$/,
|
||||
token: ['curl-prefix curl-in', 'curl-in', 'curl-in curl-value'],
|
||||
}, // Header fields ("POST /foo/bar HTTP/1.1")
|
||||
{
|
||||
regex: /^(> )([^:]+ .*)$/,
|
||||
token: ['curl-prefix curl-out curl-header', 'curl-out curl-header'],
|
||||
},
|
||||
{
|
||||
regex: /^(< )([^:]+ .*)$/,
|
||||
token: ['curl-prefix curl-in curl-header', 'curl-in curl-header'],
|
||||
}, // Data
|
||||
{
|
||||
regex: /^(\| )(.*)$/,
|
||||
token: ['curl-prefix curl-data', 'curl-data'],
|
||||
}, // Informational text
|
||||
{
|
||||
regex: /^(\* )(.*)$/,
|
||||
token: ['curl-prefix curl-comment', 'curl-comment'],
|
||||
},
|
||||
...keyValueHeaders,
|
||||
...headerFields,
|
||||
...data,
|
||||
...informationalText,
|
||||
],
|
||||
comment: [],
|
||||
meta: {},
|
||||
|
||||
@@ -5,7 +5,7 @@ import ReactDOM from 'react-dom';
|
||||
import { AUTOBIND_CFG, isMac } from '../../common/constants';
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
onKeydown?: (...args: any[]) => any;
|
||||
onKeyup?: (...args: any[]) => any;
|
||||
disabled?: boolean;
|
||||
@@ -74,7 +74,7 @@ class KeydownBinder extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.children;
|
||||
return this.props.children ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user