Options
All
  • Public
  • Public/Protected
  • All
Menu

Package cm6-graphql

CodeMirror 6 GraphQL Language extension

NPM npm downloads License Discord Channel

Provides CodeMirror 6 extension with a parser mode for GraphQL along with autocomplete and linting powered by your GraphQL Schema.

Getting Started

npm install --save cm6-graphql

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 updateSchema with the CodeMirror EditorView instance and the new schema

import { updateSchema } from 'cm6-graphql';

const onNewSchema = schema => {
  updateSchema(view, schema);
};

Index

References

IPosition

Re-exports IPosition

Position

Re-exports Position

completion

Re-exports completion

fillAllFieldsCommands

Re-exports fillAllFieldsCommands

getOpts

Re-exports getOpts

getSchema

Re-exports getSchema

graphql

Re-exports graphql

graphqlLanguage

Re-exports graphqlLanguage

graphqlLanguageSupport

Re-exports graphqlLanguageSupport

isMetaKeyPressed

Re-exports isMetaKeyPressed

jump

Re-exports jump

lint

Re-exports lint

offsetToPos

Re-exports offsetToPos

optionsStateField

Re-exports optionsStateField

posToOffset

Re-exports posToOffset

schemaStateField

Re-exports schemaStateField

showInDocsCommand

Re-exports showInDocsCommand

stateExtensions

Re-exports stateExtensions

updateOpts

Re-exports updateOpts

updateSchema

Re-exports updateSchema

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);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

optionsStateField: 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;},})

Functions

Const fillAllFieldsCommands

  • fillAllFieldsCommands(view: EditorView): boolean

Const getOpts

Const getSchema

  • getSchema(state: EditorState): void | GraphQLSchema

graphql

graphqlLanguageSupport

  • graphqlLanguageSupport(): LanguageSupport

Const isMetaKeyPressed

  • isMetaKeyPressed(e: MouseEvent): boolean

offsetToPos

  • offsetToPos(doc: Text, offset: number): Position

posToOffset

  • posToOffset(doc: Text, pos: IPosition): number

Const showInDocsCommand

  • showInDocsCommand(view: EditorView): boolean

Const stateExtensions

Const updateOpts

Const updateSchema

  • updateSchema(view: EditorView, schema?: GraphQLSchema): void

Generated using TypeDoc