187 lines
4.9 KiB
Plaintext
187 lines
4.9 KiB
Plaintext
/**
|
|
* Copyright (c) Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
/**
|
|
* Ported from codemirror-graphql
|
|
* https://github.com/graphql/codemirror-graphql/blob/master/src/info.js
|
|
*/
|
|
|
|
import type {GraphQLSchema} from 'graphql';
|
|
import type {ContextToken} from 'graphql-language-service-types';
|
|
import type {Hover} from 'vscode-languageserver-types';
|
|
import type {Position} from 'graphql-language-service-utils';
|
|
import {getTokenAtPosition, getTypeInfo} from './getAutocompleteSuggestions';
|
|
import {GraphQLNonNull, GraphQLList} from 'graphql';
|
|
|
|
export function getHoverInformation(
|
|
schema: GraphQLSchema,
|
|
queryText: string,
|
|
cursor: Position,
|
|
contextToken?: ContextToken,
|
|
): Hover.contents {
|
|
const token = contextToken || getTokenAtPosition(queryText, cursor);
|
|
|
|
if (!schema || !token || !token.state) {
|
|
return [];
|
|
}
|
|
|
|
const state = token.state;
|
|
const kind = state.kind;
|
|
const step = state.step;
|
|
const typeInfo = getTypeInfo(schema, token.state);
|
|
const options = {schema};
|
|
|
|
// Given a Schema and a Token, produce the contents of an info tooltip.
|
|
// To do this, create a div element that we will render "into" and then pass
|
|
// it to various rendering functions.
|
|
if (
|
|
(kind === 'Field' && step === 0 && typeInfo.fieldDef) ||
|
|
(kind === 'AliasedField' && step === 2 && typeInfo.fieldDef)
|
|
) {
|
|
const into = [];
|
|
renderField(into, typeInfo, options);
|
|
renderDescription(into, options, typeInfo.fieldDef);
|
|
return into.join('').trim();
|
|
} else if (kind === 'Directive' && step === 1 && typeInfo.directiveDef) {
|
|
const into = [];
|
|
renderDirective(into, typeInfo, options);
|
|
renderDescription(into, options, typeInfo.directiveDef);
|
|
return into.join('').trim();
|
|
} else if (kind === 'Argument' && step === 0 && typeInfo.argDef) {
|
|
const into = [];
|
|
renderArg(into, typeInfo, options);
|
|
renderDescription(into, options, typeInfo.argDef);
|
|
return into.join('').trim();
|
|
} else if (
|
|
kind === 'EnumValue' &&
|
|
typeInfo.enumValue &&
|
|
typeInfo.enumValue.description
|
|
) {
|
|
const into = [];
|
|
renderEnumValue(into, typeInfo, options);
|
|
renderDescription(into, options, typeInfo.enumValue);
|
|
return into.join('').trim();
|
|
} else if (
|
|
kind === 'NamedType' &&
|
|
typeInfo.type &&
|
|
typeInfo.type.description
|
|
) {
|
|
const into = [];
|
|
renderType(into, typeInfo, options, typeInfo.type);
|
|
renderDescription(into, options, typeInfo.type);
|
|
return into.join('').trim();
|
|
}
|
|
}
|
|
|
|
function renderField(into, typeInfo, options) {
|
|
renderQualifiedField(into, typeInfo, options);
|
|
renderTypeAnnotation(into, typeInfo, options, typeInfo.type);
|
|
}
|
|
|
|
function renderQualifiedField(into, typeInfo, options) {
|
|
if (!typeInfo.fieldDef) {
|
|
return;
|
|
}
|
|
const fieldName = (typeInfo.fieldDef.name: string);
|
|
if (fieldName.slice(0, 2) !== '__') {
|
|
renderType(into, typeInfo, options, typeInfo.parentType);
|
|
text(into, '.');
|
|
}
|
|
text(into, fieldName);
|
|
}
|
|
|
|
function renderDirective(into, typeInfo, options) {
|
|
if (!typeInfo.directiveDef) {
|
|
return;
|
|
}
|
|
const name = '@' + typeInfo.directiveDef.name;
|
|
text(into, name);
|
|
}
|
|
|
|
function renderArg(into, typeInfo, options) {
|
|
if (typeInfo.directiveDef) {
|
|
renderDirective(into, typeInfo, options);
|
|
} else if (typeInfo.fieldDef) {
|
|
renderQualifiedField(into, typeInfo, options);
|
|
}
|
|
|
|
if (!typeInfo.argDef) {
|
|
return;
|
|
}
|
|
|
|
const name = typeInfo.argDef.name;
|
|
text(into, '(');
|
|
text(into, name);
|
|
renderTypeAnnotation(into, typeInfo, options, typeInfo.inputType);
|
|
text(into, ')');
|
|
}
|
|
|
|
function renderTypeAnnotation(into, typeInfo, options, t) {
|
|
text(into, ': ');
|
|
renderType(into, typeInfo, options, t);
|
|
}
|
|
|
|
function renderEnumValue(into, typeInfo, options) {
|
|
if (!typeInfo.enumValue) {
|
|
return;
|
|
}
|
|
const name = typeInfo.enumValue.name;
|
|
renderType(into, typeInfo, options, typeInfo.inputType);
|
|
text(into, '.');
|
|
text(into, name);
|
|
}
|
|
|
|
function renderType(into, typeInfo, options, t) {
|
|
if (!t) {
|
|
return;
|
|
}
|
|
if (t instanceof GraphQLNonNull) {
|
|
renderType(into, typeInfo, options, t.ofType);
|
|
text(into, '!');
|
|
} else if (t instanceof GraphQLList) {
|
|
text(into, '[');
|
|
renderType(into, typeInfo, options, t.ofType);
|
|
text(into, ']');
|
|
} else {
|
|
text(into, t.name);
|
|
}
|
|
}
|
|
|
|
function renderDescription(into, options, def) {
|
|
if (!def) {
|
|
return;
|
|
}
|
|
const description =
|
|
typeof def.description === 'string' ? def.description : null;
|
|
if (description) {
|
|
text(into, '\n\n');
|
|
text(into, description);
|
|
}
|
|
renderDeprecation(into, options, def);
|
|
}
|
|
|
|
function renderDeprecation(into, options, def) {
|
|
if (!def) {
|
|
return;
|
|
}
|
|
const reason =
|
|
typeof def.deprecationReason === 'string' ? def.deprecationReason : null;
|
|
if (!reason) {
|
|
return;
|
|
}
|
|
text(into, '\n\n');
|
|
text(into, 'Deprecated: ');
|
|
text(into, reason);
|
|
}
|
|
|
|
function text(into: string[], content: string) {
|
|
into.push(content);
|
|
}
|