codemirror-graphql
Version:
GraphQL mode and helpers for CodeMirror.
125 lines (109 loc) • 3.45 kB
text/typescript
/**
* Copyright (c) 2021 GraphQL Contributors
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
*/
import CodeMirror, { Hints, Hint } from 'codemirror';
import 'codemirror/addon/hint/show-hint.js';
import { FragmentDefinitionNode, GraphQLSchema, GraphQLType } from 'graphql';
import type {
AutocompleteSuggestionOptions,
Maybe,
} from 'graphql-language-service';
import { getAutocompleteSuggestions, Position } from 'graphql-language-service';
export interface GraphQLHintOptions {
schema?: GraphQLSchema;
externalFragments?: string | FragmentDefinitionNode[];
autocompleteOptions?: AutocompleteSuggestionOptions;
}
interface IHint extends Hint {
isDeprecated?: boolean;
type?: Maybe<GraphQLType>;
description?: Maybe<string>;
deprecationReason?: Maybe<string>;
}
interface IHints extends Hints {
list: IHint[];
}
declare module 'codemirror' {
interface ShowHintOptions {
schema?: GraphQLSchema;
externalFragments?: string | FragmentDefinitionNode[];
}
interface CodeMirrorHintMap {
graphql: (
editor: CodeMirror.Editor,
options: GraphQLHintOptions,
) => IHints | undefined;
}
}
/**
* Registers a "hint" helper for CodeMirror.
*
* Using CodeMirror's "hint" addon: https://codemirror.net/demo/complete.html
* Given an editor, this helper will take the token at the cursor and return a
* list of suggested tokens.
*
* Options:
*
* - schema: GraphQLSchema provides the hinter with positionally relevant info
*
* Additional Events:
*
* - hasCompletion (codemirror, data, token) - signaled when the hinter has a
* new list of completion suggestions.
*
*/
CodeMirror.registerHelper(
'hint',
'graphql',
(
editor: CodeMirror.Editor,
options: GraphQLHintOptions,
): IHints | undefined => {
const { schema, externalFragments, autocompleteOptions } = options;
if (!schema) {
return;
}
const cur = editor.getCursor();
const token = editor.getTokenAt(cur);
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,
externalFragments,
autocompleteOptions,
);
const results = {
list: rawResults.map(item => ({
// important! for when the label is different from the insert text
text: item?.rawInsert ?? item.label,
type: item.type,
description: item.documentation,
isDeprecated: item.isDeprecated,
deprecationReason: item.deprecationReason,
})),
from: { line: cur.line, ch: tokenStart },
to: { line: cur.line, ch: token.end },
};
if (results?.list && results.list.length > 0) {
results.from = CodeMirror.Pos(results.from.line, results.from.ch);
results.to = CodeMirror.Pos(results.to.line, results.to.ch);
CodeMirror.signal(editor, 'hasCompletion', editor, results, token);
}
return results;
},
);
// exporting here so we don't need to import the codemirror show-hint addon module (and its implementation)
export type { IHint, IHints };