codemirror-graphql
Advanced tools
Comparing version 0.13.1 to 0.14.0
@@ -6,2 +6,8 @@ # Change Log | ||
# [0.14.0](https://github.com/graphql/graphiql/compare/codemirror-graphql@0.13.1...codemirror-graphql@0.14.0) (2021-01-03) | ||
### Features | ||
- merge completion logic (for implements &, variables) ([#1747](https://github.com/graphql/graphiql/issues/1747)) ([0ac0a85](https://github.com/graphql/graphiql/commit/0ac0a856cfc715d7885a9965a9a9114ef2ca4b1a)) | ||
## [0.13.1](https://github.com/graphql/graphiql/compare/codemirror-graphql@0.13.0...codemirror-graphql@0.13.1) (2020-12-28) | ||
@@ -8,0 +14,0 @@ |
239
hint.esm.js
@@ -13,5 +13,4 @@ /** | ||
import CodeMirror from 'codemirror'; | ||
import { canUseDirective, getDefinitionState, getFragmentDefinitions, getTypeInfo, hintList, objectValues } from 'graphql-language-service-interface'; | ||
import { assertAbstractType, doTypesOverlap, getNamedType, isAbstractType, isCompositeType, isInputType, isObjectType, GraphQLBoolean, GraphQLEnumType, GraphQLInterfaceType, GraphQLSchema, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, isInterfaceType } from 'graphql'; | ||
import { getAutocompleteSuggestions } from 'graphql-language-service-interface'; | ||
import { Position } from 'graphql-language-service-utils'; | ||
/** | ||
@@ -34,2 +33,3 @@ * Registers a "hint" helper for CodeMirror. | ||
*/ | ||
CodeMirror.registerHelper('hint', 'graphql', (editor, options) => { | ||
@@ -44,4 +44,5 @@ const schema = options.schema; | ||
const token = editor.getTokenAt(cur); | ||
const rawResults = getAutocompleteSuggestions(schema, editor.getValue(), token); | ||
const tokenStart = token.type !== null && /"|\w/.test(token.string[0]) ? token.start : token.end; | ||
const position = new Position(cur.line, tokenStart); | ||
const rawResults = getAutocompleteSuggestions(schema, editor.getValue(), position, token); | ||
const results = { | ||
@@ -72,230 +73,2 @@ list: rawResults.map(item => ({ | ||
return results; | ||
}); | ||
/** | ||
* Given GraphQLSchema, queryText, and context of the current position within | ||
* the source text, provide a list of typeahead entries. | ||
*/ | ||
function getAutocompleteSuggestions(schema, queryText, token) { | ||
const state = token.state.kind === 'Invalid' ? token.state.prevState : token.state; // relieve flow errors by checking if `state` exists | ||
if (!state) { | ||
return []; | ||
} | ||
const kind = state.kind; | ||
const step = state.step; | ||
const typeInfo = getTypeInfo(schema, token.state); // Definition kinds | ||
if (kind === 'Document') { | ||
return hintList(token, [{ | ||
label: 'query' | ||
}, { | ||
label: 'mutation' | ||
}, { | ||
label: 'subscription' | ||
}, { | ||
label: 'fragment' | ||
}, { | ||
label: '{' | ||
}]); | ||
} // Field names | ||
if (kind === 'SelectionSet' || kind === 'Field' || kind === 'AliasedField') { | ||
return getSuggestionsForFieldNames(token, typeInfo, schema); | ||
} // Argument names | ||
if (kind === 'Arguments' || kind === 'Argument' && step === 0) { | ||
const argDefs = typeInfo.argDefs; | ||
if (argDefs) { | ||
return hintList(token, argDefs.map(argDef => { | ||
var _argDef$description; | ||
return { | ||
label: argDef.name, | ||
type: argDef.type, | ||
documentation: (_argDef$description = argDef.description) !== null && _argDef$description !== void 0 ? _argDef$description : undefined | ||
}; | ||
})); | ||
} | ||
} // Input Object fields | ||
if (kind === 'ObjectValue' || kind === 'ObjectField' && step === 0) { | ||
if (typeInfo.objectFieldDefs) { | ||
const objectFields = objectValues(typeInfo.objectFieldDefs); | ||
return hintList(token, objectFields.map(field => { | ||
var _field$description; | ||
return { | ||
label: field.name, | ||
type: field.type, | ||
documentation: (_field$description = field.description) !== null && _field$description !== void 0 ? _field$description : undefined | ||
}; | ||
})); | ||
} | ||
} // Input values: Enum and Boolean | ||
if (kind === 'EnumValue' || kind === 'ListValue' && step === 1 || kind === 'ObjectField' && step === 2 || kind === 'Argument' && step === 2) { | ||
return getSuggestionsForInputValues(token, typeInfo); | ||
} // Fragment type conditions | ||
if (kind === 'TypeCondition' && step === 1 || kind === 'NamedType' && state.prevState != null && state.prevState.kind === 'TypeCondition') { | ||
return getSuggestionsForFragmentTypeConditions(token, typeInfo, schema); | ||
} // Fragment spread names | ||
if (kind === 'FragmentSpread' && step === 1) { | ||
return getSuggestionsForFragmentSpread(token, typeInfo, schema, queryText); | ||
} // Variable definition types | ||
if (kind === 'VariableDefinition' && step === 2 || kind === 'ListType' && step === 1 || kind === 'NamedType' && state.prevState && (state.prevState.kind === 'VariableDefinition' || state.prevState.kind === 'ListType')) { | ||
return getSuggestionsForVariableDefinition(token, schema); | ||
} // Directive names | ||
if (kind === 'Directive') { | ||
return getSuggestionsForDirective(token, state, schema); | ||
} | ||
return []; | ||
} // Helper functions to get suggestions for each kinds | ||
function getSuggestionsForFieldNames(token, typeInfo, schema) { | ||
if (typeInfo.parentType) { | ||
const parentType = typeInfo.parentType; | ||
const fields = isObjectType(parentType) || isInterfaceType(parentType) ? objectValues(parentType.getFields()) : []; | ||
if (isCompositeType(parentType)) { | ||
fields.push(TypeNameMetaFieldDef); | ||
} | ||
if (parentType === schema.getQueryType()) { | ||
fields.push(SchemaMetaFieldDef, TypeMetaFieldDef); | ||
} | ||
return hintList(token, fields.map(field => { | ||
var _field$description2; | ||
return { | ||
label: field.name, | ||
type: field.type, | ||
documentation: (_field$description2 = field.description) !== null && _field$description2 !== void 0 ? _field$description2 : undefined, | ||
isDeprecated: field.isDeprecated, | ||
deprecationReason: field.deprecationReason | ||
}; | ||
})); | ||
} | ||
return []; | ||
} | ||
function getSuggestionsForInputValues(token, typeInfo) { | ||
const namedInputType = getNamedType(typeInfo.inputType); | ||
if (namedInputType instanceof GraphQLEnumType) { | ||
const values = namedInputType.getValues(); | ||
return hintList(token, values.map(value => { | ||
var _value$description; | ||
return { | ||
label: value.name, | ||
type: namedInputType, | ||
documentation: (_value$description = value.description) !== null && _value$description !== void 0 ? _value$description : undefined, | ||
isDeprecated: value.isDeprecated, | ||
deprecationReason: value.deprecationReason | ||
}; | ||
})); | ||
} else if (namedInputType === GraphQLBoolean) { | ||
return hintList(token, [{ | ||
label: 'true', | ||
type: GraphQLBoolean, | ||
documentation: 'Not false.' | ||
}, { | ||
label: 'false', | ||
type: GraphQLBoolean, | ||
documentation: 'Not true.' | ||
}]); | ||
} | ||
return []; | ||
} | ||
function getSuggestionsForFragmentTypeConditions(token, typeInfo, schema) { | ||
let possibleTypes; | ||
if (typeInfo.parentType) { | ||
if (isAbstractType(typeInfo.parentType)) { | ||
const abstractType = assertAbstractType(typeInfo.parentType); // Collect both the possible Object types as well as the interfaces | ||
// they implement. | ||
const possibleObjTypes = schema.getPossibleTypes(abstractType); | ||
const possibleIfaceMap = Object.create(null); | ||
possibleObjTypes.forEach(type => { | ||
type.getInterfaces().forEach(iface => { | ||
possibleIfaceMap[iface.name] = iface; | ||
}); | ||
}); | ||
possibleTypes = possibleObjTypes.concat(objectValues(possibleIfaceMap)); | ||
} else { | ||
// The parent type is a non-abstract Object type, so the only possible | ||
// type that can be used is that same type. | ||
possibleTypes = [typeInfo.parentType]; | ||
} | ||
} else { | ||
const typeMap = schema.getTypeMap(); | ||
possibleTypes = objectValues(typeMap).filter(isCompositeType); | ||
} | ||
return hintList(token, possibleTypes.map(type => { | ||
const namedType = getNamedType(type); | ||
return { | ||
label: String(type), | ||
documentation: namedType && namedType.description || '' | ||
}; | ||
})); | ||
} | ||
function getSuggestionsForFragmentSpread(token, typeInfo, schema, queryText) { | ||
const typeMap = schema.getTypeMap(); | ||
const defState = getDefinitionState(token.state); | ||
const fragments = getFragmentDefinitions(queryText); // Filter down to only the fragments which may exist here. | ||
const relevantFrags = fragments.filter(frag => // Only include fragments with known types. | ||
typeMap[frag.typeCondition.name.value] && // Only include fragments which are not cyclic. | ||
!(defState && defState.kind === 'FragmentDefinition' && defState.name === frag.name.value) && // Only include fragments which could possibly be spread here. | ||
isCompositeType(typeInfo.parentType) && isCompositeType(typeMap[frag.typeCondition.name.value]) && doTypesOverlap(schema, typeInfo.parentType, typeMap[frag.typeCondition.name.value])); | ||
return hintList(token, relevantFrags.map(frag => ({ | ||
label: frag.name.value, | ||
type: typeMap[frag.typeCondition.name.value], | ||
documentation: `fragment ${frag.name.value} on ${frag.typeCondition.name.value}` | ||
}))); | ||
} | ||
function getSuggestionsForVariableDefinition(token, schema) { | ||
const inputTypeMap = schema.getTypeMap(); | ||
const inputTypes = objectValues(inputTypeMap).filter(isInputType); | ||
return hintList(token, inputTypes.map(type => ({ | ||
label: type.name, | ||
documentation: type.description | ||
}))); | ||
} | ||
function getSuggestionsForDirective(token, state, schema) { | ||
if (state.prevState && state.prevState.kind) { | ||
const directives = schema.getDirectives().filter(directive => canUseDirective(state.prevState, directive)); | ||
return hintList(token, directives.map(directive => ({ | ||
label: directive.name, | ||
documentation: directive.description || '' | ||
}))); | ||
} | ||
return []; | ||
} | ||
}); |
236
hint.js
@@ -7,3 +7,3 @@ "use strict"; | ||
var _graphql = require("graphql"); | ||
var _graphqlLanguageServiceUtils = require("graphql-language-service-utils"); | ||
@@ -50,4 +50,5 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const token = editor.getTokenAt(cur); | ||
const rawResults = getAutocompleteSuggestions(schema, editor.getValue(), token); | ||
const tokenStart = token.type !== null && /"|\w/.test(token.string[0]) ? token.start : token.end; | ||
const position = new _graphqlLanguageServiceUtils.Position(cur.line, tokenStart); | ||
const rawResults = (0, _graphqlLanguageServiceInterface.getAutocompleteSuggestions)(schema, editor.getValue(), position, token); | ||
const results = { | ||
@@ -79,231 +80,2 @@ list: rawResults.map(item => ({ | ||
return results; | ||
}); | ||
/** | ||
* Given GraphQLSchema, queryText, and context of the current position within | ||
* the source text, provide a list of typeahead entries. | ||
*/ | ||
function getAutocompleteSuggestions(schema, queryText, token) { | ||
const state = token.state.kind === 'Invalid' ? token.state.prevState : token.state; // relieve flow errors by checking if `state` exists | ||
if (!state) { | ||
return []; | ||
} | ||
const kind = state.kind; | ||
const step = state.step; | ||
const typeInfo = (0, _graphqlLanguageServiceInterface.getTypeInfo)(schema, token.state); // Definition kinds | ||
if (kind === 'Document') { | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, [{ | ||
label: 'query' | ||
}, { | ||
label: 'mutation' | ||
}, { | ||
label: 'subscription' | ||
}, { | ||
label: 'fragment' | ||
}, { | ||
label: '{' | ||
}]); | ||
} // Field names | ||
if (kind === 'SelectionSet' || kind === 'Field' || kind === 'AliasedField') { | ||
return getSuggestionsForFieldNames(token, typeInfo, schema); | ||
} // Argument names | ||
if (kind === 'Arguments' || kind === 'Argument' && step === 0) { | ||
const argDefs = typeInfo.argDefs; | ||
if (argDefs) { | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, argDefs.map(argDef => { | ||
var _argDef$description; | ||
return { | ||
label: argDef.name, | ||
type: argDef.type, | ||
documentation: (_argDef$description = argDef.description) !== null && _argDef$description !== void 0 ? _argDef$description : undefined | ||
}; | ||
})); | ||
} | ||
} // Input Object fields | ||
if (kind === 'ObjectValue' || kind === 'ObjectField' && step === 0) { | ||
if (typeInfo.objectFieldDefs) { | ||
const objectFields = (0, _graphqlLanguageServiceInterface.objectValues)(typeInfo.objectFieldDefs); | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, objectFields.map(field => { | ||
var _field$description; | ||
return { | ||
label: field.name, | ||
type: field.type, | ||
documentation: (_field$description = field.description) !== null && _field$description !== void 0 ? _field$description : undefined | ||
}; | ||
})); | ||
} | ||
} // Input values: Enum and Boolean | ||
if (kind === 'EnumValue' || kind === 'ListValue' && step === 1 || kind === 'ObjectField' && step === 2 || kind === 'Argument' && step === 2) { | ||
return getSuggestionsForInputValues(token, typeInfo); | ||
} // Fragment type conditions | ||
if (kind === 'TypeCondition' && step === 1 || kind === 'NamedType' && state.prevState != null && state.prevState.kind === 'TypeCondition') { | ||
return getSuggestionsForFragmentTypeConditions(token, typeInfo, schema); | ||
} // Fragment spread names | ||
if (kind === 'FragmentSpread' && step === 1) { | ||
return getSuggestionsForFragmentSpread(token, typeInfo, schema, queryText); | ||
} // Variable definition types | ||
if (kind === 'VariableDefinition' && step === 2 || kind === 'ListType' && step === 1 || kind === 'NamedType' && state.prevState && (state.prevState.kind === 'VariableDefinition' || state.prevState.kind === 'ListType')) { | ||
return getSuggestionsForVariableDefinition(token, schema); | ||
} // Directive names | ||
if (kind === 'Directive') { | ||
return getSuggestionsForDirective(token, state, schema); | ||
} | ||
return []; | ||
} // Helper functions to get suggestions for each kinds | ||
function getSuggestionsForFieldNames(token, typeInfo, schema) { | ||
if (typeInfo.parentType) { | ||
const parentType = typeInfo.parentType; | ||
const fields = (0, _graphql.isObjectType)(parentType) || (0, _graphql.isInterfaceType)(parentType) ? (0, _graphqlLanguageServiceInterface.objectValues)(parentType.getFields()) : []; | ||
if ((0, _graphql.isCompositeType)(parentType)) { | ||
fields.push(_graphql.TypeNameMetaFieldDef); | ||
} | ||
if (parentType === schema.getQueryType()) { | ||
fields.push(_graphql.SchemaMetaFieldDef, _graphql.TypeMetaFieldDef); | ||
} | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, fields.map(field => { | ||
var _field$description2; | ||
return { | ||
label: field.name, | ||
type: field.type, | ||
documentation: (_field$description2 = field.description) !== null && _field$description2 !== void 0 ? _field$description2 : undefined, | ||
isDeprecated: field.isDeprecated, | ||
deprecationReason: field.deprecationReason | ||
}; | ||
})); | ||
} | ||
return []; | ||
} | ||
function getSuggestionsForInputValues(token, typeInfo) { | ||
const namedInputType = (0, _graphql.getNamedType)(typeInfo.inputType); | ||
if (namedInputType instanceof _graphql.GraphQLEnumType) { | ||
const values = namedInputType.getValues(); | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, values.map(value => { | ||
var _value$description; | ||
return { | ||
label: value.name, | ||
type: namedInputType, | ||
documentation: (_value$description = value.description) !== null && _value$description !== void 0 ? _value$description : undefined, | ||
isDeprecated: value.isDeprecated, | ||
deprecationReason: value.deprecationReason | ||
}; | ||
})); | ||
} else if (namedInputType === _graphql.GraphQLBoolean) { | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, [{ | ||
label: 'true', | ||
type: _graphql.GraphQLBoolean, | ||
documentation: 'Not false.' | ||
}, { | ||
label: 'false', | ||
type: _graphql.GraphQLBoolean, | ||
documentation: 'Not true.' | ||
}]); | ||
} | ||
return []; | ||
} | ||
function getSuggestionsForFragmentTypeConditions(token, typeInfo, schema) { | ||
let possibleTypes; | ||
if (typeInfo.parentType) { | ||
if ((0, _graphql.isAbstractType)(typeInfo.parentType)) { | ||
const abstractType = (0, _graphql.assertAbstractType)(typeInfo.parentType); // Collect both the possible Object types as well as the interfaces | ||
// they implement. | ||
const possibleObjTypes = schema.getPossibleTypes(abstractType); | ||
const possibleIfaceMap = Object.create(null); | ||
possibleObjTypes.forEach(type => { | ||
type.getInterfaces().forEach(iface => { | ||
possibleIfaceMap[iface.name] = iface; | ||
}); | ||
}); | ||
possibleTypes = possibleObjTypes.concat((0, _graphqlLanguageServiceInterface.objectValues)(possibleIfaceMap)); | ||
} else { | ||
// The parent type is a non-abstract Object type, so the only possible | ||
// type that can be used is that same type. | ||
possibleTypes = [typeInfo.parentType]; | ||
} | ||
} else { | ||
const typeMap = schema.getTypeMap(); | ||
possibleTypes = (0, _graphqlLanguageServiceInterface.objectValues)(typeMap).filter(_graphql.isCompositeType); | ||
} | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, possibleTypes.map(type => { | ||
const namedType = (0, _graphql.getNamedType)(type); | ||
return { | ||
label: String(type), | ||
documentation: namedType && namedType.description || '' | ||
}; | ||
})); | ||
} | ||
function getSuggestionsForFragmentSpread(token, typeInfo, schema, queryText) { | ||
const typeMap = schema.getTypeMap(); | ||
const defState = (0, _graphqlLanguageServiceInterface.getDefinitionState)(token.state); | ||
const fragments = (0, _graphqlLanguageServiceInterface.getFragmentDefinitions)(queryText); // Filter down to only the fragments which may exist here. | ||
const relevantFrags = fragments.filter(frag => // Only include fragments with known types. | ||
typeMap[frag.typeCondition.name.value] && // Only include fragments which are not cyclic. | ||
!(defState && defState.kind === 'FragmentDefinition' && defState.name === frag.name.value) && // Only include fragments which could possibly be spread here. | ||
(0, _graphql.isCompositeType)(typeInfo.parentType) && (0, _graphql.isCompositeType)(typeMap[frag.typeCondition.name.value]) && (0, _graphql.doTypesOverlap)(schema, typeInfo.parentType, typeMap[frag.typeCondition.name.value])); | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, relevantFrags.map(frag => ({ | ||
label: frag.name.value, | ||
type: typeMap[frag.typeCondition.name.value], | ||
documentation: `fragment ${frag.name.value} on ${frag.typeCondition.name.value}` | ||
}))); | ||
} | ||
function getSuggestionsForVariableDefinition(token, schema) { | ||
const inputTypeMap = schema.getTypeMap(); | ||
const inputTypes = (0, _graphqlLanguageServiceInterface.objectValues)(inputTypeMap).filter(_graphql.isInputType); | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, inputTypes.map(type => ({ | ||
label: type.name, | ||
documentation: type.description | ||
}))); | ||
} | ||
function getSuggestionsForDirective(token, state, schema) { | ||
if (state.prevState && state.prevState.kind) { | ||
const directives = schema.getDirectives().filter(directive => (0, _graphqlLanguageServiceInterface.canUseDirective)(state.prevState, directive)); | ||
return (0, _graphqlLanguageServiceInterface.hintList)(token, directives.map(directive => ({ | ||
label: directive.name, | ||
documentation: directive.description || '' | ||
}))); | ||
} | ||
return []; | ||
} | ||
}); |
{ | ||
"name": "codemirror-graphql", | ||
"version": "0.13.1", | ||
"version": "0.14.0", | ||
"description": "GraphQL mode and helpers for CodeMirror.", | ||
@@ -50,4 +50,4 @@ "contributors": [ | ||
"dependencies": { | ||
"graphql-language-service-interface": "^2.6.0", | ||
"graphql-language-service-parser": "^1.7.0" | ||
"graphql-language-service-interface": "^2.7.0", | ||
"graphql-language-service-parser": "^1.8.0" | ||
}, | ||
@@ -66,3 +66,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "ed6c94cb8240fd89324104f0a9a40ce80a2dd71c" | ||
"gitHead": "4038cf235b71eaf9a576c86800707f204ded8865" | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
147874
3460