codemirror6教程
文章目录
两个概念
编辑器视图
视图用于展示文本的,在codemirror6
中文本信息的展示使用的是EditorView
这个类
编辑器状态
在codemirror6中,文本信息放到了EditorState
这个类,EditorState
可以展示在EditorView
之上,改变EditorView
里面的文本,可以更改页面上的文本展示。
安装Codemirror6
npm install codemirror
EditorState
编辑器状态,描述当前编辑器使用的插件,文本等信息
创建
import {EditorState,type Extension} from "@codemirror/state"
//创建编辑器状态
let state = EditorState.create({
doc: str, //这是文本
extensions:this.codemirrorPlugin //传入的插件数组
})
EditorView
编辑器视图,编辑器的展现
创建
let view = new EditorView({
state:state, //编辑器状态,编辑器视图创建时初始化的状态
parent:element //挂载的dom,可以通过parent挂载到指定的div块
})
Compartment
- 一个特殊的插件类,也叫隔层,用于封装真正的插件,当插件传入EditorState后,我们是无法直接动态改变里面的插件的,如果要改变里面的插件,就需要用到Compartment封装插件。
- Compartment就像一个隔箱一样,里面装插件
- Compartment使用场景:用户需要根据选择的语言,动态更改语法高亮。(通过Compartment去修改编辑器的高亮插件)
创建
import {Compartment} from "@codemirror/state"
import { javascript } from '@codemirror/lang-javascript'
import {EditorState,Extension} from "@codemirror/state"
const compartment = new Compartment()
let state = EditorState.create({
doc: "hello!!!", //这是文本
extensions:[
compartment.of(javascript()) //
] //传入的插件数组
})
//判断当前编辑器中是否存在当前的compartment封装过的插件
//当flag为真时,当前编辑器存在当前的compartment封装过的插件
let flag = compartment.get(view.state)
给编辑器动态的注入插件
import {EditorState,Extension, Compartment,StateEffect} from "@codemirror/state"
import {EditorView} from "@codemirror/view"
import {basicSetup} from "codemirror"
import { javascript } from '@codemirror/lang-javascript'
import {java} from '@codemirror/lang-java'
let state = EditorState.create({
doc: "hello!!!", //这是文本
extensions:[basicSetup] //传入的插件数组
})
let view = new EditorView({
state:state, //编辑器状态,编辑器视图创建时初始化的状态
parent:element //挂载的dom,可以通过parent挂载到指定的div块
})
let compartment = new Compartment()
// inject,向编辑器注入插件(如果在EditorState创建时传入,可以忽略这一步)
view.dispatch({ //通过dispatch发送事务
effects: StateEffect.appendConfig.of(compartment.of(javascript()))
})
// reconfigure,向编辑器修改某个插件
view.dispatch({
effects: compartment.reconfigure(java())
})
巧妙的封装
通过createEditorCompartment()
函数,我们就可以巧妙地封装插件
import {EditorState,type Extension, Compartment,StateEffect} from "@codemirror/state"
import {EditorView} from "@codemirror/view"
import { javascript } from '@codemirror/lang-javascript'
import {java} from '@codemirror/lang-java'
/**
* 创建一个compartment,并和对其修改的run函数
* @param view
* @returns
*/
// https://codemirror.net/examples/config/
// https://github.com/uiwjs/react-codemirror/blob/22cc81971a/src/useCodeMirror.ts#L144
// https://gist.github.com/s-cork/e7104bace090702f6acbc3004228f2cb
const createEditorCompartment = () => {
const compartment = new Compartment()
const run = (extension: Extension,view: EditorView) => {
if(compartment.get(view.state)){
//动态地重新配置插件
view.dispatch({ effects: compartment.reconfigure(extension) }) // reconfigure
}else{
//向编辑器注入某一个插件
view.dispatch({ effects: StateEffect.appendConfig.of(compartment.of(extension)) })// inject
}
}
return { compartment, run }
}
//使用
let {compartment, run} = createEditorCompartment()
//注入
run(javascript(),view)
//修改
run(java(),view)
插件
基础插件(basicSetup)
basicSetup提供了基础的插件功能,如:行数,折叠,历史记录,选择高亮,快捷键映射
import {basicSetup} from "codemirror"
EditorState.create({
doc: str, //这是文本
extensions:[basicSetup] //传入的插件数组
})
basicSetup的源码
const basicSetup = (() => [
view.lineNumbers(), //行数
view.highlightActiveLineGutter(),
view.highlightSpecialChars(),
commands.history(), //历史插件
language.foldGutter(), //折叠
view.drawSelection(),
view.dropCursor(),
state.EditorState.allowMultipleSelections.of(true), //复数选择(编辑器查找替换功能会用到)
language.indentOnInput(),
language.syntaxHighlighting(language.defaultHighlightStyle, { fallback: true }),
language.bracketMatching(),
autocomplete.closeBrackets(),
autocomplete.autocompletion(), //语法提示
view.rectangularSelection(),
view.crosshairCursor(),
view.highlightActiveLine(), //激活行高亮插件
search.highlightSelectionMatches(), //选择匹配高亮
view.keymap.of([ //一些快捷键映射
...autocomplete.closeBracketsKeymap,
...commands.defaultKeymap,
...search.searchKeymap,
...commands.historyKeymap,
...language.foldKeymap,
...autocomplete.completionKeymap,
...lint.lintKeymap
])
])();
代码高亮插件
静态高亮
如果只是高亮个别代表代码,可以通过加载不同的高亮包去高亮代码
import { javascript } from '@codemirror/lang-javascript'
EditorState.create({
doc: str, //这是文本
extensions:[javascript()] //传入的插件数组
})
动态高亮
如果需要动态加载高亮,需要引用包加载,其中languageDescription.support
指向的是高亮插件,只有当语言包加载了后languageDescription.support
才不为空
//语言包描述
import {LanguageDescription} from "@codemirror/language"
//语言包
import {languages} from "@codemirror/language-data"
//根据语言名称匹配语言描述信息
const languageDescription = LanguageDescription.matchLanguageName(languages, "java", true);
//语言高亮插件支持
let support = languageDescription.support
if(support){//已经加载
//跟新语言高亮插件支持
//...........
}else{//去加载并跟新
languageDescription.load().then(s=>{
//s是语言高亮插件
//...........
})
}
加载完语言包,还有一个重要的步骤,替换编辑器视图中的语言包,结合上面的Compartment,我们就可以很轻松的对高亮插件进行注入和修改。
//语言包描述
import {LanguageDescription} from "@codemirror/language"
//语言包
import {languages} from "@codemirror/language-data"
/**
* 创建一个compartment,并和对其修改的run函数
* @param view
* @returns
*/
// https://codemirror.net/examples/config/
// https://github.com/uiwjs/react-codemirror/blob/22cc81971a/src/useCodeMirror.ts#L144
// https://gist.github.com/s-cork/e7104bace090702f6acbc3004228f2cb
const createEditorCompartment = () => {
const compartment = new Compartment()
const run = (extension: Extension,view: EditorView) => {
if(compartment.get(view.state)){
//动态地重新配置插件
view.dispatch({ effects: compartment.reconfigure(extension) }) // reconfigure
}else{
//向编辑器注入某一个插件
view.dispatch({ effects: StateEffect.appendConfig.of(compartment.of(extension)) })// inject
}
}
return { compartment, run }
}
let state = EditorState.create({
doc: "hello!!!", //这是文本
extensions:[basicSetup] //传入的插件数组
})
let view = new EditorView({
state:state, //编辑器状态,编辑器视图创建时初始化的状态
parent:element //挂载的dom,可以通过parent挂载到指定的div块
})
//根据语言名称匹配语言描述信息
const languageDescription = LanguageDescription.matchLanguageName(languages, "java", true);
//注入Java高亮插件
languageDescription.load().then(support=>{
run(support,view)
})
在vue中的使用
<template>
<div id="editor"></div>
</template>
<script lang="ts" setup>
import {onMounted} from 'vue'
import {EditorState,Extension, Compartment,StateEffect} from "@codemirror/state"
import {EditorView} from "@codemirror/view"
import {basicSetup} from "codemirror"
/**
* 创建一个compartment,并和对其修改的run函数
* @param view
* @returns
*/
// https://codemirror.net/examples/config/
// https://github.com/uiwjs/react-codemirror/blob/22cc81971a/src/useCodeMirror.ts#L144
// https://gist.github.com/s-cork/e7104bace090702f6acbc3004228f2cb
const createEditorCompartment = () => {
const compartment = new Compartment()
const run = (extension: Extension,view: EditorView) => {
if(compartment.get(view.state)){
//动态地重新配置插件
view.dispatch({ effects: compartment.reconfigure(extension) }) // reconfigure
}else{
//向编辑器注入某一个插件
view.dispatch({ effects: StateEffect.appendConfig.of(compartment.of(extension)) })// inject
}
}
return { compartment, run }
}
//动态语言包函数
let {compartment, run } = createEditorCompartment()
let editor = null
const updateLang = (lang:string) => {
//根据语言名称匹配语言描述信息
const languageDescription = LanguageDescription.matchLanguageName(languages, "java", true);
//注入高亮插件
languageDescription.load().then(support=>{
run(support,editor)
})
}
//挂载
onMounted(() => {
let element = document.getElementById("editor")
let state = EditorState.create({
doc: "hello!!!", //这是文本
extensions:[basicSetup] //传入的插件数组
})
let view = new EditorView({
state:state, //编辑器状态,编辑器视图创建时初始化的状态
parent:element //挂载的dom,可以通过parent挂载到指定的div块
})
editor = view
})
</script>