代码编辑器库 CodeMirror 6 使用笔记

CodeMirror 已升级了 6 版本,变化较大。新版对 ts 、ES6 模块引入等的支持都很好,各个语言、插件等都单独拆成了包,可按需引入,也有一个好用的 react 包装组件。

查看文档

基础使用

安装:

npm i -S codemirror @codemirror/lang-javascript

使用:

import {basicSetup, EditorView} from "codemirror"
import {javascript} from "@codemirror/lang-javascript"

new EditorView({
  doc: "console.log('hello')\n",
  extensions: [basicSetup, javascript()],
  parent: document.body
})

react 包装组件

可使用 react 包装组件 https://uiw.gitee.io/react-codemirror/

npm i -S @uiw/react-codemirror

安装后,即可直接引入编辑器:

import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';

const editorRef = useRef(null)
<CodeMirror
  ref={editorRef}
  value="console.log('hello world!');"
  height="200px"
  extensions={[javascript()]}
  onChange={onChange}
/>

extensions 概念

可以看到,使用原生写法和 react 包装都有 extensions 这个传参。这个有点像把 5.x 版本的 mode (语言)和 addons (插件)合并到一起了,调用一个包就能完成语言支持、代码高亮、代码提示等。

组件已将各个语言设置成为了 extensions ,直接调用就可以了。我们也可以拓展一些自己的 extensions,如以下的实践示例。

获取与设置值

通过 ref 可拿到编辑器的 view 对象,进行编辑器值的获取和修改操作。

文档:CodeMirror Document Change Example

值获取与整体替换:

const getValue = () => {
  return editorRef.current?.view?.state.doc.toString() || ''
}
const setValue = (val: string) => {
  const cm = editorRef.current?.view
  cm && cm.dispatch({ changes: { from: 0, to: cm.state.doc.length, insert: val } })
}

将选中的文本替换为指定文本,如未选中时在光标处插入:

const replaceSelection = (val: string) => {
  const view = editorRef.current?.view
  view && view.dispatch(view.state.replaceSelection(val))
}

语言切换

语言切换改变传入的 extensions 即可。

const langConfigMap = {
  js: [javascript()],
  java: [java()],
  cpp: [cpp()],
}
const langKeys = Object.keys(langConfigMap)

切换:

<Select
  value={lang}
  onChange={val => setLang(val)}
  options={langKeys.map(lang => ({ value: lang, label: lang }))}
/>

传入:

<CodeMirror
    extensions={langConfigMap[lang]}
/>

值得一提的是,CodeMirror 对 JS 以外的语言支持较差,一般只能进行代码高亮识别,不支持代码提示等功能,这个在 5.x 版本中也是一样的。

自动换行

引入 codemirror 原生的 EditorView

import { EditorView } from '@codemirror/view'

在 extensions 中增加:

extensions={[...langConfigMap[lang], EditorView.lineWrapping]}

自定义代码提示

代码提示是通过 autocomplete 插件实现的。

文档中,有个示例如下:

import { CompletionContext } from '@codemirror/autocomplete'

function myCompletions(context: CompletionContext) {
  let word = context.matchBefore(/\w*/)
  if (word.from == word.to && !context.explicit)
    return null
  return {
    from: word.from,
    options: [
      {label: "match", type: "keyword"},
      {label: "hello", type: "variable", info: "(World)"},
      {label: "magic", type: "text", apply: "⠁⭒*.✩.*⭒⠁", detail: "macro"}
    ]
  }
}

以上 type 为 text 的示例即为自定义代码提示的用法,基于这个来修改即可。

使以上的代码提示作用于 js 语言:

import { javascriptLanguage } from '@codemirror/lang-javascript'
const jsSnippets = javascriptLanguage.data.of({
  autocomplete: myCompletions,
})

在 js 基础语法提示同时支持自定义的提示:

<CodeMirror
  extensions={[javascript(), jsSnippets]}
  // ... 其他设置
/>

使光标自动定位到 指定位置:

import { snippetCompletion } from '@codemirror/autocomplete'

snippetCompletion('console.log(${})', {
  label: 'log',
}),

对比同类库

比起 VSCode 开源的 monaco-editor 编辑器库,CodeMirror 体积更小,性能和兼容性更好,移动端支持更好。

本文收录于专栏
收集一些好用的前端开源库,主要是 npm 包