当前位置: 首页 > 工具软件 > CodeMirror > 使用案例 >

codemirror6教程

潘振国
2023-12-01

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>

 类似资料: