Merge branch 'develop' of github.com:getinsomnia/insomnia into develop

This commit is contained in:
Gregory Schier
2018-04-25 13:52:32 -04:00
4 changed files with 91 additions and 27 deletions

View File

@@ -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';

View File

@@ -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)) {

View File

@@ -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],

View File

@@ -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=""
/>