Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
codemirror6 react custom autocomplete

In this gist we add Custom HTML and JSON autocomplete without overriding the original autocomplete. Each language support in code mirror exports nameLanguage (eg: htmlLanguage) to allow you to add custom autocomplete.

import React, { useMemo } from 'react'
import CodeMirror from '@uiw/react-codemirror'
import { json, jsonLanguage } from '@codemirror/lang-json'
import { html, htmlLanguage } from '@codemirror/lang-html'
import { syntaxTree } from '@codemirror/language'

const getExtension=(mode)=>{
  switch (mode) {
    case "json":
      return [
        json(),
        jsonLanguage.data.of({
          autocomplete: function myCompletions(context) {
            const word = context.matchBefore(/\w*/);
            if (word.from === word.to && !context.explicit) return null;
            return {
              from: word.from,
              options: [
                {
                  label: "some_key",
                  apply: '"some_key":"value",',
                  info: "key value pair auto complete",
                },
                {
                  label: "some_key",
                  apply: '"some_key":"value",',
                  info: "yet another key value pair auto complete",
                },
              ],
            };
          },
        }),
      ];
    case "html":
      return [
        html(),
        htmlLanguage.data.of({
          autocomplete: function htmlCompletionSource(context) {
            const { state, pos } = context;
            let around = syntaxTree(state).resolveInner(pos),
              tree = around.resolve(pos, -1);
            for (
              let scan = pos, before;
              around === tree && (before = tree.childBefore(scan));

            ) {
              const last = before.lastChild;
              if (!last || !last.type.isError || last.from < last.to) break;
              around = tree = before;
              scan = last.from;
            }
            if (
              tree.name === "TagName" &&
              !(tree.parent && /CloseTag$/.test(tree.parent.name))
            ) {
              return {
                from:tree.from,
                to:pos,
                options: [
                    {
                        label:"audio",
                        info:"HTML audio tag",
                    }
                ],
                span: /^\/?[:\-\.\w\u00b7-\uffff]*$/
              };
            } 
            else if (
                (context.explicit &&
                  (tree.name === 'OpenTag' || tree.name === 'SelfClosingTag')) ||
                tree.name === 'AttributeName'
              ) {
                return {
                  from: tree.name === 'AttributeName' ? tree.from : pos,
                  to: pos,
                  options:  [
                    {
                        label:"aria-label",
                        info:"HTML aria-label attribute",
                    }
                ],
                  span: /^[:\-\.\w\u00b7-\uffff]+$/
                }
              }
          },
        }),
      ];
  }
}
const Editor = (props) => {
  const { value,mode } = props

  return (
    <div className='editorContainer'>
      <CodeMirror
        mode={mode}
        value={value}
        extensions={getExtension(mode)}
      />
    </div>
  )
}

export default Editor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment