Variables
Const completion
completion: Extension = graphqlLanguage.data.of({autocomplete(ctx: CompletionContext) {const schema = getSchema(ctx.state);const opts = getOpts(ctx.state);if (!schema) {return null;}const word = ctx.matchBefore(/\w*/);if (!word) {return null;}const lastWordChar = word.text.split('').pop()!;if (!AUTOCOMPLETE_CHARS.test(lastWordChar) && !ctx.explicit) {return null;}const val = ctx.state.doc.toString();const pos = offsetToPos(ctx.state.doc, ctx.pos);const results = getAutocompleteSuggestions(schema,val,pos,undefined,undefined,opts?.autocompleteOptions,);if (results.length === 0) {return null;}return {from: word.from,options: results.map(item => {return {label: item.label,detail: item.detail || '',info(completionData: Completion) {if (opts?.onCompletionInfoRender) {return opts.onCompletionInfoRender(item, ctx, completionData);}if (item.documentation ||(item.isDeprecated && item.deprecationReason)) {const el = document.createElement('div');el.textContent =item.documentation || item.deprecationReason || '';return el;}},};}),};},})
Const graphqlLanguage
graphqlLanguage: LRLanguage = LRLanguage.define({parser: parser.configure({props: [styleTags({Variable: t.variableName,BooleanValue: t.bool,Description: t.string,StringValue: t.string,Comment: t.lineComment,IntValue: t.integer,FloatValue: t.float,EnumValue: t.special(t.name),NullValue: t.null,DirectiveName: t.modifier,[keywords]: t.keyword,OperationType: t.definitionKeyword,FieldName: t.propertyName,Field: t.propertyName,ArgumentAttributeName: t.attributeName,Name: t.atom,'( )': t.paren,'{ }': t.brace,',': t.separator,[punctuations]: t.punctuation,}),// https://codemirror.net/docs/ref/#language.indentNodePropindentNodeProp.add({[nodesWithBraces]: delimitedIndent({ closing: '}', align: true }),}),foldNodeProp.add({[nodesWithBraces]: foldInside,}),],}),languageData: {commentTokens: { line: '#' },indentOnInput: /^\s*(\{|\})$/,},})
Const jump
jump: Extension = EditorView.domEventHandlers({click(evt, view) {const schema = getSchema(view.state);if (!schema) {return;}// TODO: Set class on cm-editor when mod key is pressed, to style cursor and tokensconst currentPosition = view.state.selection.main.head;const pos = offsetToPos(view.state.doc, currentPosition);const token = getTokenAtPosition(view.state.doc.toString(), pos);const tInfo = getTypeInfo(schema, token.state);const opts = getOpts(view.state);if (opts?.onShowInDocs && isMetaKeyPressed(evt)) {opts.onShowInDocs(tInfo.fieldDef?.name,tInfo.type?.toString(),tInfo.parentType?.toString(),);}},})
Const lint
lint: Extension = linter(view => {const schema = getSchema(view.state);const options = getOpts(view.state);if (!schema) {return [];}const validationErrors = validateSchema(schema);if (validationErrors.length) {if (!options?.showErrorOnInvalidSchema) {return [];}const combinedError = validationErrors.map(error => {return error.message;});return [{from: 0,to: view.state.doc.length,severity: 'error',message: combinedError.join('\n'),actions: [], // TODO:},];}const results = getDiagnostics(view.state.doc.toString(), schema);return results.map((item): Diagnostic | null => {if (!item.severity || !item.source) {return null;}const calculatedFrom = posToOffset(view.state.doc,new Position(item.range.start.line, item.range.start.character),);const from = Math.max(0,Math.min(calculatedFrom, view.state.doc.length),);const calculatedRo = posToOffset(view.state.doc,new Position(item.range.end.line, item.range.end.character - 1),);const to = Math.min(Math.max(from + 1, calculatedRo),view.state.doc.length,);return {from,to: from === to ? to + 1 : to,severity: SEVERITY[item.severity - 1],// source: item.source, // TODO:message: item.message,actions: [], // TODO:};}).filter((_): _ is Diagnostic => Boolean(_));},{needsRefresh(vu) {return (vu.startState.field(schemaStateField) !==vu.state.field(schemaStateField) ||vu.startState.field(optionsStateField) !==vu.state.field(optionsStateField));},},)
Const optionsStateField
options
StateField: StateField<void | GqlExtensionsOptions> = StateField.define<GqlExtensionsOptions | void>({create() {},update(opts, tr) {for (const e of tr.effects) {if (e.is(optionsEffect)) {return e.value;}}return opts;},},)
Const parser
parser: LRParser
Const schemaStateField
schemaStateField: StateField<void | GraphQLSchema> = StateField.define<GraphQLSchema | void>({create() {},update(schema, tr) {for (const e of tr.effects) {if (e.is(schemaEffect)) {return e.value;}}return schema;},})
CodeMirror 6 GraphQL Language extension
Provides CodeMirror 6 extension with a parser mode for GraphQL along with autocomplete and linting powered by your GraphQL Schema.
Getting Started
CodeMirror 6 customization is done through extensions. This package is an extension that customizes CodeMirror 6 for GraphQL.
import { basicSetup, EditorView } from 'codemirror'; import { graphql } from 'cm6-graphql'; const view = new EditorView({ doc: `mutation mutationName { setString(value: "newString") }`, extensions: [basicSetup, graphql(myGraphQLSchema)], parent: document.body, });Note: You have to provide a theme to CodeMirror 6 for the styling you want. You can take a look at this example or see the CodeMirror 6 documentation examples for more details.
Updating schema
If you need to dynamically update the GraphQL schema used in the editor, you can call
updateSchemawith the CodeMirrorEditorViewinstance and the new schemaimport { updateSchema } from 'cm6-graphql'; const onNewSchema = schema => { updateSchema(view, schema); };