mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-21 14:47:46 -04:00
Merge branch 'develop' of github.com:getinsomnia/insomnia into develop
This commit is contained in:
@@ -42,6 +42,8 @@ import 'codemirror-graphql/lint';
|
||||
import 'codemirror-graphql/mode';
|
||||
import 'codemirror-graphql/info';
|
||||
import 'codemirror-graphql/jump';
|
||||
import 'codemirror-graphql/variables/lint';
|
||||
import 'codemirror-graphql/variables/mode';
|
||||
import './modes/nunjucks';
|
||||
import './modes/curl';
|
||||
import './extensions/autocomplete';
|
||||
|
||||
@@ -540,7 +540,9 @@ class CodeEditor extends React.Component {
|
||||
_normalizeMode (mode) {
|
||||
const mimeType = mode ? mode.split(';')[0] : 'text/plain';
|
||||
|
||||
if (mimeType.includes('graphql')) {
|
||||
if (mimeType.includes('graphql-variables')) {
|
||||
return 'graphql-variables';
|
||||
} else if (mimeType.includes('graphql')) {
|
||||
// Because graphQL plugin doesn't recognize application/graphql content-type
|
||||
return 'graphql';
|
||||
} else if (this._isJSON(mimeType)) {
|
||||
|
||||
@@ -154,10 +154,14 @@ CodeMirror.defineOption('environmentAutocomplete', null, (cm, options) => {
|
||||
clearTimeout(keydownDebounce);
|
||||
});
|
||||
|
||||
// Add hot key triggers
|
||||
// Remove keymap if we're already added it
|
||||
cm.removeKeyMap('autocomplete-keymap');
|
||||
|
||||
// Add keymap
|
||||
cm.addKeyMap({
|
||||
name: 'autocomplete-keymap',
|
||||
'Ctrl-Space': completeForce, // Force autocomplete on hotkey
|
||||
"' '": completeIfAfterTagOrVarOpen
|
||||
'\' \'': completeIfAfterTagOrVarOpen
|
||||
});
|
||||
|
||||
// Close dropdown whenever something is clicked
|
||||
@@ -199,37 +203,45 @@ function hint (cm, options) {
|
||||
const nameSegmentFull = previousText;
|
||||
|
||||
// Actually try to match the list of things
|
||||
const allShortMatches = [];
|
||||
const allLongMatches = [];
|
||||
const lowPriorityMatches = [];
|
||||
const highPriorityMatches = [];
|
||||
|
||||
// Match variables
|
||||
if (allowMatchingVariables) {
|
||||
matchSegments(variablesToMatch, nameSegment, TYPE_VARIABLE, MAX_VARIABLES)
|
||||
.map(m => allShortMatches.push(m));
|
||||
.map(m => lowPriorityMatches.push(m));
|
||||
matchSegments(variablesToMatch, nameSegmentLong, TYPE_VARIABLE, MAX_VARIABLES)
|
||||
.map(m => allLongMatches.push(m));
|
||||
.map(m => highPriorityMatches.push(m));
|
||||
}
|
||||
|
||||
// Match constants (only use long segment for a more strict match)
|
||||
// TODO: Make this more flexible. This is really only here as a hack to make
|
||||
// constants only match full string prefixes.
|
||||
// Match constants
|
||||
if (allowMatchingConstants) {
|
||||
// Only match full segments with constants
|
||||
matchSegments(constantsToMatch, nameSegmentFull, TYPE_CONSTANT, MAX_CONSTANTS)
|
||||
.map(m => allLongMatches.push(m));
|
||||
const cur = cm.getCursor();
|
||||
const token = cm.getTokenAt(cur);
|
||||
|
||||
if (token.type === 'variable') {
|
||||
// For GraphQL to autocomplete constants (variables) in JSON keys
|
||||
matchSegments(constantsToMatch, nameSegment, TYPE_CONSTANT, MAX_CONSTANTS)
|
||||
.map(m => highPriorityMatches.push(m));
|
||||
} else {
|
||||
// Otherwise match full segments
|
||||
matchSegments(constantsToMatch, nameSegmentFull, TYPE_CONSTANT, MAX_CONSTANTS)
|
||||
.map(m => highPriorityMatches.push(m));
|
||||
}
|
||||
}
|
||||
|
||||
// Match tags
|
||||
if (allowMatchingTags) {
|
||||
matchSegments(tagsToMatch, nameSegment, TYPE_TAG, MAX_TAGS)
|
||||
.map(m => allShortMatches.push(m));
|
||||
.map(m => lowPriorityMatches.push(m));
|
||||
matchSegments(tagsToMatch, nameSegmentLong, TYPE_TAG, MAX_TAGS)
|
||||
.map(m => allLongMatches.push(m));
|
||||
.map(m => highPriorityMatches.push(m));
|
||||
}
|
||||
|
||||
// NOTE: This puts the longer (more precise) matches in front of the short ones
|
||||
const matches = [...allLongMatches, ...allShortMatches];
|
||||
const segment = allLongMatches.length ? nameSegmentLong : nameSegment;
|
||||
const matches = [...highPriorityMatches, ...lowPriorityMatches];
|
||||
|
||||
// Autocomplete from longest matched segment
|
||||
const segment = highPriorityMatches.length ? nameSegmentLong : nameSegment;
|
||||
|
||||
const uniqueMatches = matches.reduce(
|
||||
(arr, v) => arr.find(a => a.text === v.text) ? arr : [...arr, v],
|
||||
|
||||
@@ -4,7 +4,7 @@ import {newBodyRaw} from '../../../../models/request';
|
||||
import classnames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import autobind from 'autobind-decorator';
|
||||
import {parse, print} from 'graphql';
|
||||
import {parse, print, typeFromAST} from 'graphql';
|
||||
import {introspectionQuery} from 'graphql/utilities/introspectionQuery';
|
||||
import {buildClientSchema} from 'graphql/utilities/buildClientSchema';
|
||||
import CodeEditor from '../../codemirror/code-editor';
|
||||
@@ -124,6 +124,40 @@ class GraphQLEditor extends React.PureComponent<Props, State> {
|
||||
}
|
||||
}
|
||||
|
||||
_buildVariableTypes (schema: Object | null, query: string): {[string]: Object} {
|
||||
if (!schema) {
|
||||
return {};
|
||||
}
|
||||
|
||||
let documentAST;
|
||||
try {
|
||||
documentAST = parse(query);
|
||||
} catch (e) {
|
||||
documentAST = null;
|
||||
}
|
||||
|
||||
const definitions = documentAST ? documentAST.definitions : [];
|
||||
const variableToType = {};
|
||||
for (const {kind, variableDefinitions} of definitions) {
|
||||
if (kind !== 'OperationDefinition') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!variableDefinitions) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const {variable, type} of variableDefinitions) {
|
||||
const inputType = typeFromAST(schema, type);
|
||||
if (!inputType) {
|
||||
continue;
|
||||
}
|
||||
variableToType[variable.name.value] = inputType;
|
||||
}
|
||||
}
|
||||
return variableToType;
|
||||
}
|
||||
|
||||
async _handleRefreshSchema (): Promise<void> {
|
||||
await this._fetchAndSetSchema(this.props.request);
|
||||
}
|
||||
@@ -139,11 +173,8 @@ class GraphQLEditor extends React.PureComponent<Props, State> {
|
||||
}, 200);
|
||||
}
|
||||
|
||||
_getOperationNames (query: string): Array<string> {
|
||||
let documentAST;
|
||||
try {
|
||||
documentAST = parse(query);
|
||||
} catch (e) {
|
||||
_getOperationNames (query: string, documentAST: Object | null): Array<string> {
|
||||
if (!documentAST) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -154,7 +185,14 @@ class GraphQLEditor extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
_handleBodyChange (query: string, variables?: Object): void {
|
||||
const operationNames = this._getOperationNames(query);
|
||||
let documentAST;
|
||||
try {
|
||||
documentAST = parse(query);
|
||||
} catch (e) {
|
||||
documentAST = null;
|
||||
}
|
||||
|
||||
const operationNames = this._getOperationNames(query, documentAST);
|
||||
|
||||
const body: GraphQLBody = {query};
|
||||
|
||||
@@ -166,7 +204,11 @@ class GraphQLEditor extends React.PureComponent<Props, State> {
|
||||
body.operationName = operationNames[0];
|
||||
}
|
||||
|
||||
this.setState({variablesSyntaxError: '', body});
|
||||
this.setState({
|
||||
variablesSyntaxError: '',
|
||||
body
|
||||
});
|
||||
|
||||
this.props.onChange(GraphQLEditor._graphQLToString(body));
|
||||
}
|
||||
|
||||
@@ -274,6 +316,8 @@ class GraphQLEditor extends React.PureComponent<Props, State> {
|
||||
|
||||
const variables = prettify.json(JSON.stringify(variablesObject));
|
||||
|
||||
const variableTypes = this._buildVariableTypes(schema, query);
|
||||
|
||||
return (
|
||||
<div key={forceRefreshKey} className="graphql-editor">
|
||||
<div className="graphql-editor__query">
|
||||
@@ -336,9 +380,13 @@ class GraphQLEditor extends React.PureComponent<Props, State> {
|
||||
className={className}
|
||||
render={render}
|
||||
getRenderContext={getRenderContext}
|
||||
getAutocompleteConstants={() => Object.keys(variableTypes || {})}
|
||||
lintOptions={{
|
||||
variableToType: variableTypes
|
||||
}}
|
||||
nunjucksPowerUserMode={settings.nunjucksPowerUserMode}
|
||||
onChange={this._handleVariablesChange}
|
||||
mode="application/json"
|
||||
mode="graphql-variables"
|
||||
lineWrapping={settings.editorLineWrapping}
|
||||
placeholder=""
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user