graphql-language-service-interface
Advanced tools
Comparing version 2.9.5 to 2.9.6-canary-1a140c54.0
import { FragmentDefinitionNode, GraphQLDirective, GraphQLSchema } from 'graphql'; | ||
import { CompletionItem, AllTypeInfo, IPosition } from 'graphql-language-service-types'; | ||
import { CharacterStream, ContextToken, State, ContextTokenForCodeMirror } from 'graphql-language-service-parser'; | ||
export declare function getAutocompleteSuggestions(schema: GraphQLSchema, queryText: string, cursor: IPosition, contextToken?: ContextTokenForCodeMirror, fragmentDefs?: FragmentDefinitionNode[] | string): Array<CompletionItem>; | ||
export declare type AutocompleteSuggestionOptions = { | ||
fillLeafsOnComplete?: boolean; | ||
schema?: GraphQLSchema; | ||
}; | ||
export declare function getAutocompleteSuggestions(schema: GraphQLSchema, queryText: string, cursor: IPosition, contextToken?: ContextTokenForCodeMirror, fragmentDefs?: FragmentDefinitionNode[] | string, options?: AutocompleteSuggestionOptions): Array<CompletionItem>; | ||
export declare function getVariableCompletions(queryText: string, schema: GraphQLSchema, token: ContextToken): CompletionItem[]; | ||
@@ -6,0 +10,0 @@ export declare function getFragmentDefinitions(queryText: string): Array<FragmentDefinitionNode>; |
@@ -22,4 +22,5 @@ "use strict"; | ||
}; | ||
function getAutocompleteSuggestions(schema, queryText, cursor, contextToken, fragmentDefs) { | ||
function getAutocompleteSuggestions(schema, queryText, cursor, contextToken, fragmentDefs, options) { | ||
var _a; | ||
const opts = Object.assign(Object.assign({}, options), { schema }); | ||
const token = contextToken || getTokenAtPosition(queryText, cursor); | ||
@@ -50,3 +51,3 @@ const state = token.state.kind === 'Invalid' ? token.state.prevState : token.state; | ||
kind === graphql_language_service_parser_1.RuleKinds.ALIASED_FIELD) { | ||
return getSuggestionsForFieldNames(token, typeInfo, schema); | ||
return getSuggestionsForFieldNames(token, typeInfo, opts); | ||
} | ||
@@ -130,3 +131,23 @@ if (kind === graphql_language_service_parser_1.RuleKinds.ARGUMENTS || | ||
exports.getAutocompleteSuggestions = getAutocompleteSuggestions; | ||
function getSuggestionsForFieldNames(token, typeInfo, schema) { | ||
const insertSuffix = ` {\n $1\n}`; | ||
const getInsertText = (field) => { | ||
const type = field.type; | ||
if (graphql_2.isCompositeType(type)) { | ||
return insertSuffix; | ||
} | ||
if (graphql_1.isListType(type)) { | ||
return insertSuffix; | ||
} | ||
if (graphql_1.isNonNullType(type)) { | ||
if (graphql_1.isObjectType(type.ofType)) { | ||
return insertSuffix; | ||
} | ||
if (graphql_1.isListType(type.ofType)) { | ||
return insertSuffix; | ||
} | ||
} | ||
return null; | ||
}; | ||
function getSuggestionsForFieldNames(token, typeInfo, options) { | ||
var _a; | ||
if (typeInfo.parentType) { | ||
@@ -141,3 +162,3 @@ const parentType = typeInfo.parentType; | ||
} | ||
if (parentType === schema.getQueryType()) { | ||
if (parentType === ((_a = options === null || options === void 0 ? void 0 : options.schema) === null || _a === void 0 ? void 0 : _a.getQueryType())) { | ||
fields.push(graphql_2.SchemaMetaFieldDef, graphql_2.TypeMetaFieldDef); | ||
@@ -147,3 +168,6 @@ } | ||
var _a; | ||
return ({ | ||
if (field.name === 'securityAdvisories') { | ||
console.log(field); | ||
} | ||
const suggestion = { | ||
sortText: String(index) + field.name, | ||
@@ -158,3 +182,9 @@ label: field.name, | ||
type: field.type, | ||
}); | ||
}; | ||
const insertText = getInsertText(field); | ||
if (insertText) { | ||
suggestion.insertText = field.name + insertText; | ||
suggestion.insertTextFormat = vscode_languageserver_types_1.InsertTextFormat.Snippet; | ||
} | ||
return suggestion; | ||
})); | ||
@@ -357,6 +387,6 @@ } | ||
if (!definitions[variableName]) { | ||
const label = token.string === '$' ? variableName : '$' + variableName; | ||
definitions[variableName] = { | ||
detail: variableType.toString(), | ||
label, | ||
insertText: token.string === '$' ? variableName : '$' + variableName, | ||
label: variableName, | ||
type: variableType, | ||
@@ -648,3 +678,5 @@ kind: vscode_languageserver_types_1.CompletionItemKind.Variable, | ||
enumType instanceof graphql_2.GraphQLEnumType | ||
? find(enumType.getValues(), (val) => val.value === state.name) | ||
? enumType | ||
.getValues() | ||
.find((val) => val.value === state.name) | ||
: null; | ||
@@ -690,10 +722,2 @@ break; | ||
exports.getTypeInfo = getTypeInfo; | ||
function find(array, predicate) { | ||
for (let i = 0; i < array.length; i++) { | ||
if (predicate(array[i])) { | ||
return array[i]; | ||
} | ||
} | ||
return null; | ||
} | ||
//# sourceMappingURL=getAutocompleteSuggestions.js.map |
@@ -6,4 +6,4 @@ export * from './autocompleteUtils'; | ||
export { getOutline } from './getOutline'; | ||
export { getHoverInformation } from './getHoverInformation'; | ||
export { getHoverInformation, HoverConfig } from './getHoverInformation'; | ||
export * from './GraphQLLanguageService'; | ||
//# sourceMappingURL=index.d.ts.map |
import { FragmentDefinitionNode, GraphQLDirective, GraphQLSchema } from 'graphql'; | ||
import { CompletionItem, AllTypeInfo, IPosition } from 'graphql-language-service-types'; | ||
import { CharacterStream, ContextToken, State, ContextTokenForCodeMirror } from 'graphql-language-service-parser'; | ||
export declare function getAutocompleteSuggestions(schema: GraphQLSchema, queryText: string, cursor: IPosition, contextToken?: ContextTokenForCodeMirror, fragmentDefs?: FragmentDefinitionNode[] | string): Array<CompletionItem>; | ||
export declare type AutocompleteSuggestionOptions = { | ||
fillLeafsOnComplete?: boolean; | ||
schema?: GraphQLSchema; | ||
}; | ||
export declare function getAutocompleteSuggestions(schema: GraphQLSchema, queryText: string, cursor: IPosition, contextToken?: ContextTokenForCodeMirror, fragmentDefs?: FragmentDefinitionNode[] | string, options?: AutocompleteSuggestionOptions): Array<CompletionItem>; | ||
export declare function getVariableCompletions(queryText: string, schema: GraphQLSchema, token: ContextToken): CompletionItem[]; | ||
@@ -6,0 +10,0 @@ export declare function getFragmentDefinitions(queryText: string): Array<FragmentDefinitionNode>; |
@@ -1,3 +0,3 @@ | ||
import { CompletionItemKind } from 'vscode-languageserver-types'; | ||
import { isInterfaceType, GraphQLInterfaceType, GraphQLObjectType, Kind, DirectiveLocation, } from 'graphql'; | ||
import { CompletionItemKind, InsertTextFormat, } from 'vscode-languageserver-types'; | ||
import { isInterfaceType, GraphQLInterfaceType, GraphQLObjectType, Kind, DirectiveLocation, isObjectType, isListType, isNonNullType, } from 'graphql'; | ||
import { GraphQLBoolean, GraphQLEnumType, GraphQLInputObjectType, GraphQLList, SchemaMetaFieldDef, TypeMetaFieldDef, TypeNameMetaFieldDef, assertAbstractType, doTypesOverlap, getNamedType, getNullableType, isAbstractType, isCompositeType, isInputType, visit, parse, } from 'graphql'; | ||
@@ -19,4 +19,5 @@ import { CharacterStream, onlineParser, RuleKinds, } from 'graphql-language-service-parser'; | ||
}; | ||
export function getAutocompleteSuggestions(schema, queryText, cursor, contextToken, fragmentDefs) { | ||
export function getAutocompleteSuggestions(schema, queryText, cursor, contextToken, fragmentDefs, options) { | ||
var _a; | ||
const opts = Object.assign(Object.assign({}, options), { schema }); | ||
const token = contextToken || getTokenAtPosition(queryText, cursor); | ||
@@ -47,3 +48,3 @@ const state = token.state.kind === 'Invalid' ? token.state.prevState : token.state; | ||
kind === RuleKinds.ALIASED_FIELD) { | ||
return getSuggestionsForFieldNames(token, typeInfo, schema); | ||
return getSuggestionsForFieldNames(token, typeInfo, opts); | ||
} | ||
@@ -126,3 +127,23 @@ if (kind === RuleKinds.ARGUMENTS || | ||
} | ||
function getSuggestionsForFieldNames(token, typeInfo, schema) { | ||
const insertSuffix = ` {\n $1\n}`; | ||
const getInsertText = (field) => { | ||
const type = field.type; | ||
if (isCompositeType(type)) { | ||
return insertSuffix; | ||
} | ||
if (isListType(type)) { | ||
return insertSuffix; | ||
} | ||
if (isNonNullType(type)) { | ||
if (isObjectType(type.ofType)) { | ||
return insertSuffix; | ||
} | ||
if (isListType(type.ofType)) { | ||
return insertSuffix; | ||
} | ||
} | ||
return null; | ||
}; | ||
function getSuggestionsForFieldNames(token, typeInfo, options) { | ||
var _a; | ||
if (typeInfo.parentType) { | ||
@@ -137,3 +158,3 @@ const parentType = typeInfo.parentType; | ||
} | ||
if (parentType === schema.getQueryType()) { | ||
if (parentType === ((_a = options === null || options === void 0 ? void 0 : options.schema) === null || _a === void 0 ? void 0 : _a.getQueryType())) { | ||
fields.push(SchemaMetaFieldDef, TypeMetaFieldDef); | ||
@@ -143,3 +164,6 @@ } | ||
var _a; | ||
return ({ | ||
if (field.name === 'securityAdvisories') { | ||
console.log(field); | ||
} | ||
const suggestion = { | ||
sortText: String(index) + field.name, | ||
@@ -154,3 +178,9 @@ label: field.name, | ||
type: field.type, | ||
}); | ||
}; | ||
const insertText = getInsertText(field); | ||
if (insertText) { | ||
suggestion.insertText = field.name + insertText; | ||
suggestion.insertTextFormat = InsertTextFormat.Snippet; | ||
} | ||
return suggestion; | ||
})); | ||
@@ -353,6 +383,6 @@ } | ||
if (!definitions[variableName]) { | ||
const label = token.string === '$' ? variableName : '$' + variableName; | ||
definitions[variableName] = { | ||
detail: variableType.toString(), | ||
label, | ||
insertText: token.string === '$' ? variableName : '$' + variableName, | ||
label: variableName, | ||
type: variableType, | ||
@@ -639,3 +669,5 @@ kind: CompletionItemKind.Variable, | ||
enumType instanceof GraphQLEnumType | ||
? find(enumType.getValues(), (val) => val.value === state.name) | ||
? enumType | ||
.getValues() | ||
.find((val) => val.value === state.name) | ||
: null; | ||
@@ -680,10 +712,2 @@ break; | ||
} | ||
function find(array, predicate) { | ||
for (let i = 0; i < array.length; i++) { | ||
if (predicate(array[i])) { | ||
return array[i]; | ||
} | ||
} | ||
return null; | ||
} | ||
//# sourceMappingURL=getAutocompleteSuggestions.js.map |
@@ -6,4 +6,4 @@ export * from './autocompleteUtils'; | ||
export { getOutline } from './getOutline'; | ||
export { getHoverInformation } from './getHoverInformation'; | ||
export { getHoverInformation, HoverConfig } from './getHoverInformation'; | ||
export * from './GraphQLLanguageService'; | ||
//# sourceMappingURL=index.d.ts.map |
{ | ||
"name": "graphql-language-service-interface", | ||
"version": "2.9.5", | ||
"version": "2.9.6-canary-1a140c54.0", | ||
"description": "Interface to the GraphQL Language Service", | ||
@@ -34,3 +34,3 @@ "contributors": [ | ||
"graphql-language-service-types": "^1.8.6", | ||
"graphql-language-service-utils": "^2.6.3", | ||
"graphql-language-service-utils": "^2.7.0-canary-1a140c54.0", | ||
"vscode-languageserver-types": "^3.15.1" | ||
@@ -37,0 +37,0 @@ }, |
@@ -24,2 +24,46 @@ /** | ||
const expectedResults = { | ||
droid: { | ||
label: 'droid', | ||
detail: 'Droid', | ||
insertTextFormat: 2, | ||
insertText: `droid {\n $1\n}`, | ||
}, | ||
hero: { | ||
label: 'hero', | ||
detail: 'Character', | ||
insertTextFormat: 2, | ||
insertText: `hero {\n $1\n}`, | ||
}, | ||
human: { | ||
label: 'human', | ||
detail: 'Human', | ||
insertTextFormat: 2, | ||
insertText: `human {\n $1\n}`, | ||
}, | ||
inputTypeTest: { | ||
label: 'inputTypeTest', | ||
detail: 'TestType', | ||
insertTextFormat: 2, | ||
insertText: `inputTypeTest {\n $1\n}`, | ||
}, | ||
appearsIn: { | ||
label: 'appearsIn', | ||
detail: '[Episode]', | ||
insertTextFormat: 2, | ||
insertText: `appearsIn {\n $1\n}`, | ||
}, | ||
friends: { | ||
label: 'friends', | ||
detail: '[Character]', | ||
insertTextFormat: 2, | ||
insertText: `friends {\n $1\n}`, | ||
}, | ||
}; | ||
const suggestionCommand = { | ||
command: 'editor.action.triggerSuggest', | ||
title: 'Suggestions', | ||
}; | ||
describe('getAutocompleteSuggestions', () => { | ||
@@ -54,2 +98,4 @@ let schema: GraphQLSchema; | ||
.map(suggestion => { | ||
// TODO: A PR where we do `const { type, ..rest} = suggestion; return rest;` | ||
// and validate the entire completion object - kinds, documentation, etc | ||
const response = { label: suggestion.label } as CompletionItem; | ||
@@ -59,432 +105,470 @@ if (suggestion.detail) { | ||
} | ||
// if(suggestion.documentation) { | ||
// response.documentation = String(suggestion.documentation) | ||
// } | ||
if (suggestion.insertText) { | ||
response.insertText = suggestion.insertText; | ||
} | ||
if (suggestion.insertTextFormat) { | ||
response.insertTextFormat = suggestion.insertTextFormat; | ||
} | ||
if (suggestion.command) { | ||
response.command = suggestion.command; | ||
} | ||
return response; | ||
}); | ||
} | ||
describe('with Operation types', () => { | ||
it('provides correct sortText response', () => { | ||
const result = getAutocompleteSuggestions( | ||
schema, | ||
`{ h`, | ||
new Position(0, 3), | ||
).map(({ sortText, label, detail }) => ({ sortText, label, detail })); | ||
expect(result).toEqual([ | ||
{ | ||
sortText: '0hero', | ||
label: 'hero', | ||
detail: 'Character', | ||
}, | ||
it('provides correct sortText response', () => { | ||
const result = getAutocompleteSuggestions( | ||
schema, | ||
`{ h`, | ||
new Position(0, 3), | ||
).map(({ sortText, label, detail }) => ({ sortText, label, detail })); | ||
expect(result).toEqual([ | ||
{ | ||
sortText: '0hero', | ||
label: 'hero', | ||
detail: 'Character', | ||
}, | ||
{ | ||
sortText: '1human', | ||
label: 'human', | ||
detail: 'Human', | ||
}, | ||
{ | ||
sortText: '1human', | ||
label: 'human', | ||
detail: 'Human', | ||
}, | ||
{ | ||
sortText: '6__schema', | ||
label: '__schema', | ||
detail: '__Schema!', | ||
}, | ||
]); | ||
}); | ||
{ | ||
sortText: '6__schema', | ||
label: '__schema', | ||
detail: '__Schema!', | ||
}, | ||
]); | ||
}); | ||
it('provides correct initial keywords', () => { | ||
expect(testSuggestions('', new Position(0, 0))).toEqual([ | ||
{ label: '{' }, | ||
{ label: 'fragment' }, | ||
{ label: 'mutation' }, | ||
{ label: 'query' }, | ||
{ label: 'subscription' }, | ||
]); | ||
it('provides correct initial keywords', () => { | ||
expect(testSuggestions('', new Position(0, 0))).toEqual([ | ||
{ label: '{' }, | ||
{ label: 'fragment' }, | ||
{ label: 'mutation' }, | ||
{ label: 'query' }, | ||
{ label: 'subscription' }, | ||
]); | ||
expect(testSuggestions('q', new Position(0, 1))).toEqual([ | ||
{ label: '{' }, | ||
{ label: 'query' }, | ||
]); | ||
}); | ||
expect(testSuggestions('q', new Position(0, 1))).toEqual([ | ||
{ label: '{' }, | ||
{ label: 'query' }, | ||
]); | ||
}); | ||
it('provides correct suggestions at where the cursor is', () => { | ||
// Below should provide initial keywords | ||
expect(testSuggestions(' {}', new Position(0, 0))).toEqual([ | ||
{ label: '{' }, | ||
{ label: 'fragment' }, | ||
{ label: 'mutation' }, | ||
{ label: 'query' }, | ||
{ label: 'subscription' }, | ||
]); | ||
it('provides correct suggestions at where the cursor is', () => { | ||
// Below should provide initial keywords | ||
expect(testSuggestions(' {}', new Position(0, 0))).toEqual([ | ||
{ label: '{' }, | ||
{ label: 'fragment' }, | ||
{ label: 'mutation' }, | ||
{ label: 'query' }, | ||
{ label: 'subscription' }, | ||
]); | ||
// Below should provide root field names | ||
expect(testSuggestions(' {}', new Position(0, 2))).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
expectedResults.droid, | ||
expectedResults.hero, | ||
expectedResults.human, | ||
expectedResults.inputTypeTest, | ||
]); | ||
// Below should provide root field names | ||
expect(testSuggestions(' {}', new Position(0, 2))).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
{ label: 'droid', detail: 'Droid' }, | ||
{ label: 'hero', detail: 'Character' }, | ||
{ label: 'human', detail: 'Human' }, | ||
{ label: 'inputTypeTest', detail: 'TestType' }, | ||
]); | ||
// Test for query text with empty lines | ||
expect( | ||
testSuggestions( | ||
` | ||
query name { | ||
...testFragment | ||
} | ||
`, | ||
new Position(2, 0), | ||
), | ||
).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
expectedResults.droid, | ||
expectedResults.hero, | ||
expectedResults.human, | ||
expectedResults.inputTypeTest, | ||
]); | ||
}); | ||
// Test for query text with empty lines | ||
expect( | ||
testSuggestions( | ||
` | ||
query name { | ||
...testFragment | ||
} | ||
`, | ||
new Position(2, 0), | ||
), | ||
).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
{ label: 'droid', detail: 'Droid' }, | ||
{ label: 'hero', detail: 'Character' }, | ||
{ label: 'human', detail: 'Human' }, | ||
{ label: 'inputTypeTest', detail: 'TestType' }, | ||
]); | ||
}); | ||
it('provides correct field name suggestions', () => { | ||
const result = testSuggestions('{ ', new Position(0, 2)); | ||
expect(result).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
expectedResults.droid, | ||
expectedResults.hero, | ||
expectedResults.human, | ||
expectedResults.inputTypeTest, | ||
]); | ||
}); | ||
it('provides correct field name suggestions', () => { | ||
const result = testSuggestions('{ ', new Position(0, 2)); | ||
expect(result).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
{ label: 'droid', detail: 'Droid' }, | ||
{ label: 'hero', detail: 'Character' }, | ||
{ label: 'human', detail: 'Human' }, | ||
{ label: 'inputTypeTest', detail: 'TestType' }, | ||
]); | ||
}); | ||
it('provides correct field name suggestions after filtered', () => { | ||
const result = testSuggestions('{ h ', new Position(0, 3)); | ||
expect(result).toEqual([expectedResults.hero, expectedResults.human]); | ||
}); | ||
it('provides correct field name suggestions after filtered', () => { | ||
const result = testSuggestions('{ h ', new Position(0, 3)); | ||
expect(result).toEqual([ | ||
{ label: 'hero', detail: 'Character' }, | ||
{ label: 'human', detail: 'Human' }, | ||
]); | ||
}); | ||
it('provides correct field name suggestions with alias', () => { | ||
const result = testSuggestions( | ||
'{ alias: human(id: "1") { ', | ||
new Position(0, 26), | ||
); | ||
it('provides correct field name suggestions with alias', () => { | ||
const result = testSuggestions( | ||
'{ alias: human(id: "1") { ', | ||
new Position(0, 26), | ||
); | ||
expect(result).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
expectedResults.appearsIn, | ||
expectedResults.friends, | ||
{ label: 'id', detail: 'String!' }, | ||
{ label: 'name', detail: 'String' }, | ||
{ label: 'secretBackstory', detail: 'String' }, | ||
]); | ||
}); | ||
expect(result).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
{ label: 'appearsIn', detail: '[Episode]' }, | ||
{ label: 'friends', detail: '[Character]' }, | ||
{ label: 'id', detail: 'String!' }, | ||
{ label: 'name', detail: 'String' }, | ||
{ label: 'secretBackstory', detail: 'String' }, | ||
]); | ||
}); | ||
it('provides correct field suggestions for fragments', () => { | ||
const result = testSuggestions( | ||
'fragment test on Human { ', | ||
new Position(0, 25), | ||
); | ||
it('provides correct field suggestions for fragments', () => { | ||
const result = testSuggestions( | ||
'fragment test on Human { ', | ||
new Position(0, 25), | ||
); | ||
expect(result).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
expectedResults.appearsIn, | ||
expectedResults.friends, | ||
{ label: 'id', detail: 'String!' }, | ||
{ label: 'name', detail: 'String' }, | ||
{ label: 'secretBackstory', detail: 'String' }, | ||
]); | ||
}); | ||
expect(result).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
{ label: 'appearsIn', detail: '[Episode]' }, | ||
{ label: 'friends', detail: '[Character]' }, | ||
{ label: 'id', detail: 'String!' }, | ||
{ label: 'name', detail: 'String' }, | ||
{ label: 'secretBackstory', detail: 'String' }, | ||
]); | ||
}); | ||
it('provides correct argument suggestions', () => { | ||
const result = testSuggestions('{ human (', new Position(0, 9)); | ||
expect(result).toEqual([ | ||
{ | ||
label: 'id', | ||
detail: 'String!', | ||
insertText: 'id: ', | ||
command: suggestionCommand, | ||
}, | ||
]); | ||
}); | ||
it('provides correct argument suggestions', () => { | ||
const result = testSuggestions('{ human (', new Position(0, 9)); | ||
expect(result).toEqual([{ label: 'id', detail: 'String!' }]); | ||
}); | ||
it('provides correct argument suggestions when using aliases', () => { | ||
const result = testSuggestions( | ||
'{ aliasTest: human( ', | ||
new Position(0, 20), | ||
); | ||
expect(result).toEqual([ | ||
{ | ||
label: 'id', | ||
detail: 'String!', | ||
command: suggestionCommand, | ||
insertText: 'id: ', | ||
}, | ||
]); | ||
}); | ||
it('provides correct argument suggestions when using aliases', () => { | ||
const result = testSuggestions('{ aliasTest: human( ', new Position(0, 20)); | ||
expect(result).toEqual([{ label: 'id', detail: 'String!' }]); | ||
}); | ||
it('provides correct input type suggestions', () => { | ||
const result = testSuggestions( | ||
'query($exampleVariable: ) { ', | ||
new Position(0, 24), | ||
); | ||
expect(result).toEqual([ | ||
{ label: '__DirectiveLocation' }, | ||
{ label: '__TypeKind' }, | ||
{ label: 'Boolean' }, | ||
{ label: 'Episode' }, | ||
{ label: 'InputType' }, | ||
{ label: 'Int' }, | ||
{ label: 'String' }, | ||
]); | ||
}); | ||
it('provides correct input type suggestions', () => { | ||
const result = testSuggestions( | ||
'query($exampleVariable: ) { ', | ||
new Position(0, 24), | ||
); | ||
expect(result).toEqual([ | ||
{ label: '__DirectiveLocation' }, | ||
{ label: '__TypeKind' }, | ||
{ label: 'Boolean' }, | ||
{ label: 'Episode' }, | ||
{ label: 'InputType' }, | ||
{ label: 'Int' }, | ||
{ label: 'String' }, | ||
]); | ||
}); | ||
it('provides filtered input type suggestions', () => { | ||
const result = testSuggestions( | ||
'query($exampleVariable: In) { ', | ||
new Position(0, 26), | ||
); | ||
expect(result).toEqual([ | ||
{ label: '__DirectiveLocation' }, | ||
{ label: '__TypeKind' }, | ||
{ label: 'InputType' }, | ||
{ label: 'Int' }, | ||
{ label: 'String' }, | ||
]); | ||
}); | ||
it('provides filtered input type suggestions', () => { | ||
const result = testSuggestions( | ||
'query($exampleVariable: In) { ', | ||
new Position(0, 26), | ||
); | ||
expect(result).toEqual([ | ||
{ label: '__DirectiveLocation' }, | ||
{ label: '__TypeKind' }, | ||
{ label: 'InputType' }, | ||
{ label: 'Int' }, | ||
{ label: 'String' }, | ||
]); | ||
}); | ||
it('provides correct typeCondition suggestions', () => { | ||
const suggestionsOnQuery = testSuggestions( | ||
'{ ... on ', | ||
new Position(0, 9), | ||
); | ||
expect( | ||
suggestionsOnQuery.filter(({ label }) => !label.startsWith('__')), | ||
).toEqual([{ label: 'Query' }]); | ||
it('provides correct typeCondition suggestions', () => { | ||
const suggestionsOnQuery = testSuggestions('{ ... on ', new Position(0, 9)); | ||
expect( | ||
suggestionsOnQuery.filter(({ label }) => !label.startsWith('__')), | ||
).toEqual([{ label: 'Query' }]); | ||
const suggestionsOnCompositeType = testSuggestions( | ||
'{ hero(episode: JEDI) { ... on } }', | ||
new Position(0, 31), | ||
); | ||
const suggestionsOnCompositeType = testSuggestions( | ||
'{ hero(episode: JEDI) { ... on } }', | ||
new Position(0, 31), | ||
); | ||
expect(suggestionsOnCompositeType).toEqual([ | ||
{ label: 'Character' }, | ||
{ label: 'Droid' }, | ||
{ label: 'Human' }, | ||
]); | ||
expect(suggestionsOnCompositeType).toEqual([ | ||
{ label: 'Character' }, | ||
{ label: 'Droid' }, | ||
{ label: 'Human' }, | ||
]); | ||
expect( | ||
testSuggestions( | ||
'fragment Foo on Character { ... on }', | ||
new Position(0, 35), | ||
), | ||
).toEqual([ | ||
{ label: 'Character' }, | ||
{ label: 'Droid' }, | ||
{ label: 'Human' }, | ||
]); | ||
}); | ||
expect( | ||
testSuggestions( | ||
'fragment Foo on Character { ... on }', | ||
new Position(0, 35), | ||
), | ||
).toEqual([{ label: 'Character' }, { label: 'Droid' }, { label: 'Human' }]); | ||
}); | ||
it('provides correct typeCondition suggestions on fragment', () => { | ||
const result = testSuggestions('fragment Foo on {}', new Position(0, 16)); | ||
expect(result.filter(({ label }) => !label.startsWith('__'))).toEqual([ | ||
{ label: 'AnotherInterface' }, | ||
{ label: 'Character' }, | ||
{ label: 'Droid' }, | ||
{ label: 'Human' }, | ||
{ label: 'Query' }, | ||
{ label: 'TestInterface' }, | ||
{ label: 'TestType' }, | ||
]); | ||
}); | ||
it('provides correct typeCondition suggestions on fragment', () => { | ||
const result = testSuggestions('fragment Foo on {}', new Position(0, 16)); | ||
expect(result.filter(({ label }) => !label.startsWith('__'))).toEqual([ | ||
{ label: 'AnotherInterface' }, | ||
{ label: 'Character' }, | ||
{ label: 'Droid' }, | ||
{ label: 'Human' }, | ||
{ label: 'Query' }, | ||
{ label: 'TestInterface' }, | ||
{ label: 'TestType' }, | ||
]); | ||
}); | ||
it('provides correct enum suggestions', () => { | ||
const result = testSuggestions('{ hero(episode: ', new Position(0, 16)); | ||
expect(result).toEqual([ | ||
{ label: 'EMPIRE', detail: 'Episode' }, | ||
{ label: 'JEDI', detail: 'Episode' }, | ||
{ label: 'NEWHOPE', detail: 'Episode' }, | ||
]); | ||
}); | ||
it('provides correct ENUM suggestions', () => { | ||
const result = testSuggestions('{ hero(episode: ', new Position(0, 16)); | ||
expect(result).toEqual([ | ||
{ label: 'EMPIRE', detail: 'Episode' }, | ||
{ label: 'JEDI', detail: 'Episode' }, | ||
{ label: 'NEWHOPE', detail: 'Episode' }, | ||
]); | ||
}); | ||
it('provides correct suggestions for declared variables upon typing $', () => { | ||
const result = testSuggestions( | ||
'query($id: String, $ep: Episode!){ hero(episode: $ }', | ||
new Position(0, 51), | ||
); | ||
expect(result).toEqual([ | ||
{ label: 'ep', insertText: '$ep', detail: 'Episode' }, | ||
]); | ||
}); | ||
it('provides correct suggestions for declared variables upon typing $', () => { | ||
const result = testSuggestions( | ||
'query($id: String, $ep: Episode!){ hero(episode: $ }', | ||
new Position(0, 51), | ||
); | ||
expect(result).toEqual([{ label: '$ep', detail: 'Episode' }]); | ||
}); | ||
it('provides correct suggestions for variables based on argument context', () => { | ||
const result = testSuggestions( | ||
'query($id: String!, $episode: Episode!){ hero(episode: ', | ||
new Position(0, 55), | ||
); | ||
expect(result).toEqual([ | ||
{ label: 'EMPIRE', detail: 'Episode' }, | ||
{ label: 'episode', detail: 'Episode', insertText: '$episode' }, | ||
{ label: 'JEDI', detail: 'Episode' }, | ||
{ label: 'NEWHOPE', detail: 'Episode' }, | ||
// no $id here, it's not compatible :P | ||
]); | ||
}); | ||
it('provides correct suggestions for variables based on argument context', () => { | ||
const result = testSuggestions( | ||
'query($id: String!, $episode: Episode!){ hero(episode: ', | ||
new Position(0, 55), | ||
); | ||
expect(result).toEqual([ | ||
{ label: '$episode', detail: 'Episode' }, | ||
{ label: 'EMPIRE', detail: 'Episode' }, | ||
{ label: 'JEDI', detail: 'Episode' }, | ||
{ label: 'NEWHOPE', detail: 'Episode' }, | ||
// no $id here, it's not compatible :P | ||
]); | ||
}); | ||
it('provides fragment name suggestion', () => { | ||
const fragmentDef = 'fragment Foo on Human { id }'; | ||
it('provides fragment name suggestion', () => { | ||
const fragmentDef = 'fragment Foo on Human { id }'; | ||
// Test on concrete types | ||
expect( | ||
testSuggestions( | ||
`${fragmentDef} query { human(id: "1") { ...`, | ||
new Position(0, 57), | ||
), | ||
).toEqual([{ label: 'Foo', detail: 'Human' }]); | ||
expect( | ||
testSuggestions( | ||
`query { human(id: "1") { ... }} ${fragmentDef}`, | ||
new Position(0, 28), | ||
), | ||
).toEqual([{ label: 'Foo', detail: 'Human' }]); | ||
// Test on concrete types | ||
expect( | ||
testSuggestions( | ||
`${fragmentDef} query { human(id: "1") { ...`, | ||
new Position(0, 57), | ||
), | ||
).toEqual([{ label: 'Foo', detail: 'Human' }]); | ||
expect( | ||
testSuggestions( | ||
`query { human(id: "1") { ... }} ${fragmentDef}`, | ||
// Test on abstract type | ||
expect( | ||
testSuggestions( | ||
`${fragmentDef} query { hero(episode: JEDI) { ...`, | ||
new Position(0, 62), | ||
), | ||
).toEqual([{ label: 'Foo', detail: 'Human' }]); | ||
}); | ||
it('provides correct fragment name suggestions for external fragments', () => { | ||
const externalFragments = parse(` | ||
fragment CharacterDetails on Human { | ||
name | ||
} | ||
fragment CharacterDetails2 on Human { | ||
name | ||
} | ||
`).definitions as FragmentDefinitionNode[]; | ||
const result = testSuggestions( | ||
'query { human(id: "1") { ... }}', | ||
new Position(0, 28), | ||
), | ||
).toEqual([{ label: 'Foo', detail: 'Human' }]); | ||
externalFragments, | ||
); | ||
// Test on abstract type | ||
expect( | ||
testSuggestions( | ||
`${fragmentDef} query { hero(episode: JEDI) { ...`, | ||
new Position(0, 62), | ||
), | ||
).toEqual([{ label: 'Foo', detail: 'Human' }]); | ||
}); | ||
expect(result).toEqual([ | ||
{ label: 'CharacterDetails', detail: 'Human' }, | ||
{ label: 'CharacterDetails2', detail: 'Human' }, | ||
]); | ||
}); | ||
it('provides correct fragment name suggestions for external fragments', () => { | ||
const externalFragments = parse(` | ||
fragment CharacterDetails on Human { | ||
name | ||
} | ||
fragment CharacterDetails2 on Human { | ||
name | ||
} | ||
`).definitions as FragmentDefinitionNode[]; | ||
it('provides correct directive suggestions', () => { | ||
expect(testSuggestions('{ test @ }', new Position(0, 8))).toEqual([ | ||
{ label: 'include' }, | ||
{ label: 'skip' }, | ||
{ label: 'stream' }, | ||
{ label: 'test' }, | ||
]); | ||
const result = testSuggestions( | ||
'query { human(id: "1") { ... }}', | ||
new Position(0, 28), | ||
externalFragments, | ||
); | ||
expect(testSuggestions('{ test @', new Position(0, 8))).toEqual([ | ||
{ label: 'include' }, | ||
{ label: 'skip' }, | ||
{ label: 'stream' }, | ||
{ label: 'test' }, | ||
]); | ||
expect(result).toEqual([ | ||
{ label: 'CharacterDetails', detail: 'Human' }, | ||
{ label: 'CharacterDetails2', detail: 'Human' }, | ||
]); | ||
}); | ||
expect( | ||
testSuggestions('{ aliasTest: test @ }', new Position(0, 19)), | ||
).toEqual([ | ||
{ label: 'include' }, | ||
{ label: 'skip' }, | ||
{ label: 'stream' }, | ||
{ label: 'test' }, | ||
]); | ||
it('provides correct directive suggestions', () => { | ||
expect(testSuggestions('{ test @ }', new Position(0, 8))).toEqual([ | ||
{ label: 'include' }, | ||
{ label: 'skip' }, | ||
{ label: 'stream' }, | ||
{ label: 'test' }, | ||
]); | ||
expect(testSuggestions('query @', new Position(0, 7))).toEqual([]); | ||
}); | ||
expect(testSuggestions('{ test @', new Position(0, 8))).toEqual([ | ||
{ label: 'include' }, | ||
{ label: 'skip' }, | ||
{ label: 'stream' }, | ||
{ label: 'test' }, | ||
]); | ||
it('provides correct testInput suggestions', () => { | ||
expect( | ||
testSuggestions('{ inputTypeTest(args: {', new Position(0, 23)), | ||
).toEqual([ | ||
{ label: 'key', detail: 'String!' }, | ||
{ label: 'value', detail: 'Int' }, | ||
]); | ||
}); | ||
expect( | ||
testSuggestions('{ aliasTest: test @ }', new Position(0, 19)), | ||
).toEqual([ | ||
{ label: 'include' }, | ||
{ label: 'skip' }, | ||
{ label: 'stream' }, | ||
{ label: 'test' }, | ||
]); | ||
it('provides correct field name suggestion inside inline fragment', () => { | ||
expect( | ||
testSuggestions( | ||
'fragment Foo on Character { ... on Human { }}', | ||
new Position(0, 42), | ||
), | ||
).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
expectedResults.appearsIn, | ||
expectedResults.friends, | ||
{ label: 'id', detail: 'String!' }, | ||
{ label: 'name', detail: 'String' }, | ||
{ label: 'secretBackstory', detail: 'String' }, | ||
]); | ||
expect(testSuggestions('query @', new Position(0, 7))).toEqual([]); | ||
// Typeless inline fragment assumes the type automatically | ||
expect( | ||
testSuggestions('fragment Foo on Droid { ... { ', new Position(0, 30)), | ||
).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
expectedResults.appearsIn, | ||
expectedResults.friends, | ||
{ label: 'id', detail: 'String!' }, | ||
{ label: 'name', detail: 'String' }, | ||
{ label: 'primaryFunction', detail: 'String' }, | ||
{ label: 'secretBackstory', detail: 'String' }, | ||
]); | ||
}); | ||
}); | ||
describe('with SDL types', () => { | ||
it('provides correct directive suggestions on definitions', () => | ||
expect(testSuggestions('type Type @', new Position(0, 11))).toEqual([ | ||
{ label: 'onAllDefs' }, | ||
])); | ||
it('provides correct suggestions on object fields', () => | ||
expect( | ||
testSuggestions(`type Type {\n aField: s`, new Position(0, 23)), | ||
).toEqual([{ label: 'Episode' }, { label: 'String' }])); | ||
it('provides correct suggestions on input object fields', () => | ||
expect( | ||
testSuggestions(`input Type {\n aField: s`, new Position(0, 23)), | ||
).toEqual([{ label: 'Episode' }, { label: 'String' }])); | ||
it('provides correct directive suggestions on args definitions', () => | ||
expect( | ||
testSuggestions('type Type { field(arg: String @', new Position(0, 31)), | ||
).toEqual([ | ||
{ label: 'deprecated' }, | ||
{ label: 'onAllDefs' }, | ||
{ label: 'onArg' }, | ||
])); | ||
it('provides correct testInput suggestions', () => { | ||
expect( | ||
testSuggestions('{ inputTypeTest(args: {', new Position(0, 23)), | ||
).toEqual([ | ||
{ label: 'key', detail: 'String!' }, | ||
{ label: 'value', detail: 'Int' }, | ||
]); | ||
}); | ||
it('provides correct interface suggestions when extending with an interface', () => | ||
expect( | ||
testSuggestions('type Type implements ', new Position(0, 20)), | ||
).toEqual([ | ||
{ label: 'AnotherInterface' }, | ||
{ label: 'Character' }, | ||
{ label: 'TestInterface' }, | ||
])); | ||
it('provides correct field name suggestion inside inline fragment', () => { | ||
expect( | ||
testSuggestions( | ||
'fragment Foo on Character { ... on Human { }}', | ||
new Position(0, 42), | ||
), | ||
).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
{ label: 'appearsIn', detail: '[Episode]' }, | ||
{ label: 'friends', detail: '[Character]' }, | ||
{ label: 'id', detail: 'String!' }, | ||
{ label: 'name', detail: 'String' }, | ||
{ label: 'secretBackstory', detail: 'String' }, | ||
]); | ||
// Typeless inline fragment assumes the type automatically | ||
expect( | ||
testSuggestions('fragment Foo on Droid { ... { ', new Position(0, 30)), | ||
).toEqual([ | ||
{ label: '__typename', detail: 'String!' }, | ||
{ label: 'appearsIn', detail: '[Episode]' }, | ||
{ label: 'friends', detail: '[Character]' }, | ||
{ label: 'id', detail: 'String!' }, | ||
{ label: 'name', detail: 'String' }, | ||
{ label: 'primaryFunction', detail: 'String' }, | ||
{ label: 'secretBackstory', detail: 'String' }, | ||
]); | ||
it('provides correct interface suggestions when extending a type with multiple interfaces', () => | ||
expect( | ||
testSuggestions( | ||
'type Type implements TestInterface & ', | ||
new Position(0, 37), | ||
), | ||
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }])); | ||
it('provides correct interface suggestions when extending an interface with multiple interfaces', () => | ||
expect( | ||
testSuggestions( | ||
'interface IExample implements TestInterface & ', | ||
new Position(0, 46), | ||
), | ||
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }])); | ||
it('provides filtered interface suggestions when extending an interface with multiple interfaces', () => | ||
expect( | ||
testSuggestions( | ||
'interface IExample implements TestInterface & Inter', | ||
new Position(0, 48), | ||
), | ||
).toEqual([{ label: 'AnotherInterface' }])); | ||
it('provides no interface suggestions when using implements and there are no & or { characters present', () => | ||
expect( | ||
testSuggestions( | ||
'interface IExample implements TestInterface ', | ||
new Position(0, 44), | ||
), | ||
).toEqual([])); | ||
it('provides fragment completion after a list of interfaces to extend', () => | ||
expect( | ||
testSuggestions( | ||
'interface IExample implements TestInterface & AnotherInterface @f', | ||
new Position(0, 65), | ||
), | ||
).toEqual([{ label: 'onAllDefs' }])); | ||
it('provides correct interface suggestions when extending an interface with an inline interface', () => | ||
expect( | ||
testSuggestions( | ||
'interface A { id: String }\ninterface MyInterface implements ', | ||
new Position(1, 33), | ||
), | ||
).toEqual([ | ||
{ label: 'A' }, | ||
{ label: 'AnotherInterface' }, | ||
{ label: 'Character' }, | ||
{ label: 'TestInterface' }, | ||
])); | ||
}); | ||
it('provides correct directive suggestions on definitions', () => | ||
expect(testSuggestions('type Type @', new Position(0, 11))).toEqual([ | ||
{ label: 'onAllDefs' }, | ||
])); | ||
it('provides correct directive suggestions on args definitions', () => | ||
expect( | ||
testSuggestions('type Type { field(arg: String @', new Position(0, 31)), | ||
).toEqual([ | ||
{ label: 'deprecated' }, | ||
{ label: 'onAllDefs' }, | ||
{ label: 'onArg' }, | ||
])); | ||
it('provides correct interface suggestions when extending with an interface', () => | ||
expect( | ||
testSuggestions('type Type implements ', new Position(0, 20)), | ||
).toEqual([ | ||
{ label: 'AnotherInterface' }, | ||
{ label: 'Character' }, | ||
{ label: 'TestInterface' }, | ||
])); | ||
it('provides correct interface suggestions when extending a type with multiple interfaces', () => | ||
expect( | ||
testSuggestions( | ||
'type Type implements TestInterface & ', | ||
new Position(0, 37), | ||
), | ||
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }])); | ||
it('provides correct interface suggestions when extending an interface with multiple interfaces', () => | ||
expect( | ||
testSuggestions( | ||
'interface IExample implements TestInterface & ', | ||
new Position(0, 46), | ||
), | ||
).toEqual([{ label: 'AnotherInterface' }, { label: 'Character' }])); | ||
it('provides filtered interface suggestions when extending an interface with multiple interfaces', () => | ||
expect( | ||
testSuggestions( | ||
'interface IExample implements TestInterface & Inter', | ||
new Position(0, 48), | ||
), | ||
).toEqual([{ label: 'AnotherInterface' }])); | ||
it('provides no interface suggestions when using implements and there are no & or { characters present', () => | ||
expect( | ||
testSuggestions( | ||
'interface IExample implements TestInterface ', | ||
new Position(0, 44), | ||
), | ||
).toEqual([])); | ||
it('provides fragment completion after a list of interfaces to extend', () => | ||
expect( | ||
testSuggestions( | ||
'interface IExample implements TestInterface & AnotherInterface @f', | ||
new Position(0, 65), | ||
), | ||
).toEqual([{ label: 'onAllDefs' }])); | ||
it('provides correct interface suggestions when extending an interface with an inline interface', () => | ||
expect( | ||
testSuggestions( | ||
'interface A { id: String }\ninterface MyInterface implements ', | ||
new Position(1, 33), | ||
), | ||
).toEqual([ | ||
{ label: 'A' }, | ||
{ label: 'AnotherInterface' }, | ||
{ label: 'Character' }, | ||
{ label: 'TestInterface' }, | ||
])); | ||
}); |
@@ -9,3 +9,6 @@ /** | ||
*/ | ||
import { CompletionItemKind } from 'vscode-languageserver-types'; | ||
import { | ||
CompletionItemKind, | ||
InsertTextFormat, | ||
} from 'vscode-languageserver-types'; | ||
@@ -28,2 +31,5 @@ import { | ||
GraphQLArgument, | ||
isObjectType, | ||
isListType, | ||
isNonNullType, | ||
} from 'graphql'; | ||
@@ -92,2 +98,7 @@ | ||
export type AutocompleteSuggestionOptions = { | ||
fillLeafsOnComplete?: boolean; | ||
schema?: GraphQLSchema; | ||
}; | ||
/** | ||
@@ -103,3 +114,8 @@ * Given GraphQLSchema, queryText, and context of the current position within | ||
fragmentDefs?: FragmentDefinitionNode[] | string, | ||
options?: AutocompleteSuggestionOptions, | ||
): Array<CompletionItem> { | ||
const opts = { | ||
...options, | ||
schema, | ||
}; | ||
const token: ContextToken = | ||
@@ -118,3 +134,2 @@ contextToken || getTokenAtPosition(queryText, cursor); | ||
const step = state.step; | ||
const typeInfo = getTypeInfo(schema, token.state); | ||
@@ -152,3 +167,3 @@ // Definition kinds | ||
) { | ||
return getSuggestionsForFieldNames(token, typeInfo, schema); | ||
return getSuggestionsForFieldNames(token, typeInfo, opts); | ||
} | ||
@@ -279,2 +294,23 @@ | ||
const insertSuffix = ` {\n $1\n}`; | ||
const getInsertText = (field: GraphQLField<null, null>) => { | ||
const type = field.type; | ||
if (isCompositeType(type)) { | ||
return insertSuffix; | ||
} | ||
if (isListType(type)) { | ||
return insertSuffix; | ||
} | ||
if (isNonNullType(type)) { | ||
if (isObjectType(type.ofType)) { | ||
return insertSuffix; | ||
} | ||
if (isListType(type.ofType)) { | ||
return insertSuffix; | ||
} | ||
} | ||
return null; | ||
}; | ||
// Helper functions to get suggestions for each kinds | ||
@@ -284,3 +320,3 @@ function getSuggestionsForFieldNames( | ||
typeInfo: AllTypeInfo, | ||
schema: GraphQLSchema, | ||
options?: AutocompleteSuggestionOptions, | ||
): Array<CompletionItem> { | ||
@@ -300,3 +336,3 @@ if (typeInfo.parentType) { | ||
} | ||
if (parentType === schema.getQueryType()) { | ||
if (parentType === options?.schema?.getQueryType()) { | ||
fields.push(SchemaMetaFieldDef, TypeMetaFieldDef); | ||
@@ -306,14 +342,27 @@ } | ||
token, | ||
fields.map<CompletionItem>((field, index) => ({ | ||
// This will sort the fields in the same order they are listed in the schema | ||
sortText: String(index) + field.name, | ||
label: field.name, | ||
detail: String(field.type), | ||
documentation: field.description ?? undefined, | ||
deprecated: Boolean(field.deprecationReason), | ||
isDeprecated: Boolean(field.deprecationReason), | ||
deprecationReason: field.deprecationReason, | ||
kind: CompletionItemKind.Field, | ||
type: field.type, | ||
})), | ||
fields.map<CompletionItem>((field, index) => { | ||
if (field.name === 'securityAdvisories') { | ||
console.log(field); | ||
} | ||
const suggestion: CompletionItem = { | ||
// This will sort the fields in the same order they are listed in the schema | ||
sortText: String(index) + field.name, | ||
label: field.name, | ||
detail: String(field.type), | ||
documentation: field.description ?? undefined, | ||
deprecated: Boolean(field.deprecationReason), | ||
isDeprecated: Boolean(field.deprecationReason), | ||
deprecationReason: field.deprecationReason, | ||
kind: CompletionItemKind.Field, | ||
type: field.type, | ||
}; | ||
// TODO: fillLeafs capability | ||
const insertText = getInsertText(field); | ||
if (insertText) { | ||
suggestion.insertText = field.name + insertText; | ||
suggestion.insertTextFormat = InsertTextFormat.Snippet; | ||
} | ||
return suggestion; | ||
}), | ||
); | ||
@@ -625,6 +674,7 @@ } | ||
// append `$` if the `token.string` is not already `$` | ||
const label = token.string === '$' ? variableName : '$' + variableName; | ||
definitions[variableName] = { | ||
detail: variableType.toString(), | ||
label, | ||
insertText: token.string === '$' ? variableName : '$' + variableName, | ||
label: variableName, // keep label the same for `codemirror-graphql` | ||
type: variableType, | ||
@@ -952,2 +1002,3 @@ kind: CompletionItemKind.Variable, | ||
break; | ||
// TODO: needs more tests | ||
case RuleKinds.ALIASED_FIELD: { | ||
@@ -987,2 +1038,3 @@ const name = state.prevState && state.prevState.name; | ||
break; | ||
// TODO: needs tests | ||
case RuleKinds.ENUM_VALUE: | ||
@@ -992,8 +1044,8 @@ const enumType = getNamedType(inputType as GraphQLType); | ||
enumType instanceof GraphQLEnumType | ||
? find( | ||
enumType.getValues() as GraphQLEnumValue[], | ||
(val: GraphQLEnumValue) => val.value === state.name, | ||
) | ||
? enumType | ||
.getValues() | ||
.find((val: GraphQLEnumValue) => val.value === state.name) | ||
: null; | ||
break; | ||
// TODO: needs tests | ||
case RuleKinds.LIST_VALUE: | ||
@@ -1011,2 +1063,3 @@ const nullableType = getNullableType(inputType as GraphQLType); | ||
break; | ||
// TODO: needs tests | ||
case RuleKinds.OBJECT_FIELD: | ||
@@ -1016,2 +1069,3 @@ const objectField = | ||
inputType = objectField && objectField.type; | ||
break; | ||
@@ -1044,11 +1098,1 @@ case RuleKinds.NAMED_TYPE: | ||
} | ||
// Returns the first item in the array which causes predicate to return truthy. | ||
function find(array: any[], predicate: Function) { | ||
for (let i = 0; i < array.length; i++) { | ||
if (predicate(array[i])) { | ||
return array[i]; | ||
} | ||
} | ||
return null; | ||
} |
@@ -19,4 +19,4 @@ /** | ||
export { getOutline } from './getOutline'; | ||
export { getHoverInformation } from './getHoverInformation'; | ||
export { getHoverInformation, HoverConfig } from './getHoverInformation'; | ||
export * from './GraphQLLanguageService'; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
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
391374
6825
2