使用 CodeMirror6 创建代码编辑器

0 阅读2分钟

1、安装依赖

安装codemirror依赖包:

$ pnpm install codemirror

2、创建挂载点

新建codeMirror.vue文件:

<template>
  <div ref="codeEditor"></div>
</template>

3、最小编辑器

使用minimalSetup模块创建:

// codeMirror.vue
<script setup>
import { minimalSetup, EditorView } from 'codemirror'
import { onMounted, ref } from 'vue'

const codeEditor = ref()
onMounted(() => {
  new EditorView({
    doc: 'console.log("hello, world")',
    extensions: [minimalSetup],
    parent: codeEditor.value
  })
})
</script>

效果如下:

image.png

4、编辑器简要入门

CodeMirror 编辑器可以分为两个主要组件:

  • EditorState 是一个状态对象,用于维护构成文档的数据结构和修改。

  • EditorView 是一个显示适配器,它将状态转换为可以看到并与之交互的内容,它还将这些交互转换为状态更新。

  • codemirror 包提供 EditorView,但它不提供 EditorState。相反,它提供了两个扩展包(minimalSetup 和 basicSetup),旨在通过牺牲一些可配置性来快速入门。

  • 通常,extensions 属性直接提供给 EditorState,但当没有单独的状态实例时,可以将其作为快捷方式传递给 EditorView

  • minimalSetup只包含一些你可能需要的核心扩展。用于创建功能编辑器的最小扩展集。仅包括默认键盘映射  撤消历史记录  特殊字符突出显示  自定义选择绘制默认突出显示样式 

  • 如果想从 codemirror 包中获得更完整的体验,应该使用 basicSetup 模块。

5、简易编辑器

使用basicSetup模块创建:basicSetup 模块确实预先配置了很多漂亮的功能。最不需要真正将它们联系在一起的是语言模块。语言模块包括用于标记代码的解析器、用于设置标记样式的主题以及用于快捷方式(如注释掉行)的命令。

// codeMirror.vue
<script setup>
import { basicSetup, EditorView } from 'codemirror'
import { onMounted, ref } from 'vue'

const codeEditor = ref()
onMounted(() => {
  new EditorView({
    doc: 'console.log("hello, world")',
    extensions: [basicSetup],
    parent: codeEditor.value
  })
})
</script>

效果如下:自带行号等功能...

image.png

6、语言包: JavaScript 包

6.1 安装

$ pnpm install @codemirror/lang-javascript

6.2 导入

导入 javascript 模块并将其添加到 extensions 数组中:

// codeMirror.vue
<script setup>
import { basicSetup, EditorView } from 'codemirror'
import { javascript } from '@codemirror/lang-javascript'//引入
import { onMounted, ref } from 'vue'

const codeEditor = ref()
onMounted(() => {
  new EditorView({
    doc: 'console.log("hello, world")',
    extensions: [
      basicSetup,
      javascript()//加到`extensions` 数组中
    ],
    parent: codeEditor.value
  })
})
</script>

测试是否生效:使用 Cmd + / 或 Ctrl + /(许多编辑器中的默认设置)来切换行注释。

7、将 EditorState 与 EditorView 结合使用

7.1 安装

$ pnpm install @codemirror/state @codemirror/view

7.2 替换 EditorView

使用从@codemirror/view 导入的 EditorView:

// codeMirror.vue
<script setup>
import { basicSetup } from 'codemirror'
import { EditorView } from '@codemirror/view'//引入
import { javascript } from '@codemirror/lang-javascript'
import { onMounted, ref } from 'vue'

const codeEditor = ref()
onMounted(() => {
  new EditorView({
    doc: 'console.log("hello, world")',
    extensions: [
      basicSetup,
      javascript()
    ],
    parent: codeEditor.value
  })
})
</script>

7.3 导入 EditorState

  • 导入 EditorState 并将其实例传递给 EditorView下的 state 属性。
  • 与 EditorView 不同,EditorState 具有私有构造函数,因此必须使用 EditorState.create 对其进行初始化。
  • 将 doc 和 extensions 属性移动到 EditorState 进行初始值定义。
  • parent 由 EditorView 管理,因为它与状态的显示方式直接相关。
<script setup>
// codeMirror.vue
import { basicSetup } from 'codemirror'
import { EditorView } from '@codemirror/view'
import { EditorState } from '@codemirror/state'//引入
import { javascript } from '@codemirror/lang-javascript'
import { onMounted, ref } from 'vue'

const codeEditor = ref()
onMounted(() => {
  new EditorView({
    parent: targetElement,
    state: EditorState.create({
      doc: initialText,
      extensions: [basicSetup, javascript()],
    })
  })
})
</script>

8、自定义拓展

basicSetup不可配置。将 basicSetup 替换为其关联的模块。

8.1 安装

安装组成 basicSetup 模块的拓展:

$ pnpm install @codemirror/autocomplete @codemirror/commands @codemirror/language @codemirror/lint @codemirror/search

8.2 导入并注册

<script setup>
// codeMirror.vue
import { basicSetup } from 'codemirror'
import { EditorState } from '@codemirror/state'
import { javascript } from '@codemirror/lang-javascript'
import { onMounted, ref } from 'vue'
import { autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap } from '@codemirror/autocomplete'
import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
import { bracketMatching, defaultHighlightStyle, foldGutter, foldKeymap, indentOnInput, syntaxHighlighting } from '@codemirror/language'
import { lintKeymap } from '@codemirror/lint'
import { highlightSelectionMatches, searchKeymap } from '@codemirror/search'
import { crosshairCursor, drawSelection, dropCursor, EditorView, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, keymap, lineNumbers, rectangularSelection } from '@codemirror/view'

const codeEditor = ref()
onMounted(() => {
  new EditorView({
    parent: targetElement,
    state: EditorState.create({
      doc: initialText,
      extensions: [
          lineNumbers(),
          highlightActiveLineGutter(),
          highlightSpecialChars(),
          history(),
          foldGutter(),
          drawSelection(),
          dropCursor(),
          EditorState.allowMultipleSelections.of(true),
          indentOnInput(),
          syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
          bracketMatching(),
          closeBrackets(),
          autocompletion(),
          rectangularSelection(),
          crosshairCursor(),
          highlightActiveLine(),
          highlightSelectionMatches(),
          keymap.of([
            ...closeBracketsKeymap,
            ...defaultKeymap,
            ...searchKeymap,
            ...historyKeymap,
            ...foldKeymap,
            ...completionKeymap,
            ...lintKeymap,
          ]),
          javascript(),
        ],
    })
  })
})
</script>

一切正常的话,效果和5一样;

9、只读编辑器

官方案例:Read-Only Editor

//基于7的代码进行修改:
<script setup>
// codeMirror.vue
import { basicSetup } from 'codemirror'
import { EditorView } from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { javascript } from '@codemirror/lang-javascript'
import { onMounted, ref } from 'vue'

const codeEditor = ref()
onMounted(() => {
  new EditorView({
    parent: targetElement,
    state: EditorState.create({
      doc: initialText,
      extensions: [basicSetup, javascript(), EditorState.readOnly.of(true)],// EditorState.readOnly.of(true)
    })
  })
})
</script>

10、获取编辑器内容

<script setup>
// codeMirror.vue
import { basicSetup } from 'codemirror'
import { EditorView } from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { javascript } from '@codemirror/lang-javascript'
import { onMounted, ref } from 'vue'

const codeEditor = ref()
let Editor = ref() //编辑器实例
onMounted(() => {
    Editor.value = new EditorView({
        parent: targetElement,
        state: EditorState.create({
          doc: initialText,
          extensions: [basicSetup, javascript(), EditorState.readOnly.of(true)],
        })
    })
})
//获取编辑器内容
function getCode() {
  return Editor.value.state.doc.toString()
}
</script>

11、更新编辑器内容

官方案例:Document Changes

<script setup>
// codeMirror.vue
import { basicSetup } from 'codemirror'
import { EditorView } from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { javascript } from '@codemirror/lang-javascript'
import { onMounted, ref } from 'vue'

const codeEditor = ref()
let Editor = ref() //编辑器实例
onMounted(() => {
    Editor.value = new EditorView({
        parent: targetElement,
        state: EditorState.create({
          doc: initialText,
          extensions: [basicSetup, javascript(), EditorState.readOnly.of(true)],
        })
    })
})
//获取编辑器内容
function getCode() {
  return Editor.value.state.doc.toString()
}
// 更新编辑器的内容
function update() {
  const ins = Editor.value
  ins.dispatch({
    changes: {
      from: 0,
      to: ins.state.doc.length, // 结束的位置
      insert: `待展示的内容`
    }
  })
}
</script>

12、配置主题

官方案例:Styling

13、自动语言检查

官方案例:Automatic Language Detection