-
Notifications
You must be signed in to change notification settings - Fork 533
自定义语法
sunsonliu edited this page Nov 6, 2023
·
10 revisions
通过一个例子来了解cherry的自定义语法机制,如下:
定义一个自定义语法:
/**
* 自定义一个语法,识别形如 ***ABC*** 的内容,并将其替换成 <span style="color: red"><strong>ABC</strong></span>
*/
var CustomHookA = Cherry.createSyntaxHook('important', Cherry.constants.HOOKS_TYPE_LIST.SEN, {
makeHtml(str) {
return str.replace(this.RULE.reg, function(whole, m1, m2) {
return `<span style="color: red;"><strong>${m2}</strong></span>`;
});
},
rule(str) {
return { reg: /(\*\*\*)([^\*]+)\1/g };
},
});
...
/**
* @param {string} hookName 语法名
* @param {string} type 语法类型,行内语法为Cherry.constants.HOOKS_TYPE_LIST.SEN,段落语法为Cherry.constants.HOOKS_TYPE_LIST.PAR
* @param {object} options 自定义语法的主体逻辑
*/
Cherry.createSyntaxHook(hookName, type, options)将这个语法配置到cherry配置中:
new Cherry({
id: 'markdown-container',
value: '## hello world',
fileUpload: myFileUpload,
customSyntax: {
importHook: {
syntaxClass: CustomHookA, // 将自定义语法对象挂载到 importHook.syntaxClass上
force: false, // true: 当cherry自带的语法中也有一个“importHook”时,用自定义的语法覆盖默认语法; false:不覆盖
before: 'fontEmphasis', // 定义该自定义语法的执行顺序,当前例子表明在加粗/斜体语法前执行该自定义语法
},
},
toolbars: {
...
},
});效果如下图:

原理: 一言以蔽之,cherry的语法解析引擎就是将一堆正则按一定顺序依次执行,将markdown字符串替换为html字符串的工具。
语法分两类:
- 行内语法,即类似加粗、斜体、上下标等主要对文字样式进行控制的语法,最大的特点是可以相互嵌套
- 段落语法,即类似表格、代码块、列表等主要对整段文本样式进行控制的语法,有两个特点:
- 可以在内部执行行内语法
- 可以声明与其他段落语法互斥
语法的组成:
- 语法名,唯一的作用就是用来定义语法的执行顺序的时候按语法名排序
- beforeMakeHtml(),engine会最先按语法正序依次调用beforeMakeHtml()
- makeHtml(),engine会在调用完所有语法的beforeMakeHtml()后,再按语法正序依次调用makeHtml()
- afterMakeHtml(),engine会在调用完所有语法的makeHtml()后,再按语法逆序依次调用afterMakeHtml()
- rule(),用来定义语法的正则
- needCache,用来声明是否需要“缓存”,只有段落语法支持这个变量,true:段落语法可以在beforeMakeHtml()、makeHtml()的时候利用
this.pushCache()和this.popCache()实现排它的能力
自带的语法:
- 行内Hook
引擎会按当前顺序执行makeHtml方法- emoji 表情
- image 图片
- link 超链接
- autoLink 自动超链接(自动将符合超链接格式的字符串转换成标签)
- fontEmphasis 加粗和斜体
- bgColor 字体背景色
- fontColor 字体颜色
- fontSize 字体大小
- sub 下标
- sup 上标
- ruby 一种表明上下排列的排版方式,典型应用就是文字上面加拼音
- strikethrough 删除线
- underline 下划线
- highLight 高亮(就是在文字外层包一个标签)
- 段落级 Hook
引擎会按当前排序顺序执行beforeMake、makeHtml方法
引擎会按当前排序逆序执行afterMake方法- codeBlock 代码块
- inlineCode 行内代码(因要实现排它特性,所以归类为段落语法)
- mathBlock 块级公式
- inlineMath 行内公式(理由同行内代码)
- htmlBlock html标签,主要作用为过滤白名单外的html标签
- footnote 脚注
- commentReference 超链接引用
- br 换行
- table 表格
- blockquote 引用
- toc 目录
- header 标题
- hr 分割线
- list 有序列表、无序列表、checklist
- detail 手风琴
- panel 信息面板
- normalParagraph 普通段落
具体介绍:
- 如果要实现一个行内语法,只需要了解以下三个概念
- 定义正则 rule()
- 定义具体的正则替换逻辑 makeHtml()
- 确定自定义语法名,并确定执行顺序
- 如果要实现一个段落语法,则需要在了解行内语法相关概念后再了解以下概念:
- 排它机制
- 编辑区和预览区同步滚动机制
- 局部渲染机制
由于上面已有自定义行内语法的实现例子,接下来我们将通过实现一个自定义段落语法的例子来了解各个机制
最简单段落语法
/**
* 把 \n++\n XXX \n++\n 渲染成 <div>XXX</div>
*/
var myBlockHook = Cherry.createSyntaxHook('myBlock', Cherry.constants.HOOKS_TYPE_LIST.PAR, {
makeHtml(str) {
return str.replace(this.RULE.reg, function(whole, m1, m2) {
return `<div style="border: 1px solid;border-radius: 15px;background: gold;">${m2}</div>`;
});
},
rule(str) {
return { reg: /\n\+\+(\n[\s\S]+?\n)\+\+\n/g };
},
});
...
new Cherry({
...
customSyntax: {
myBlock: {
syntaxClass: myBlockHook,
before: 'blockquote',
},
},
...
});效果如下:

当我们尝试进行段逻语法嵌套时,就会发现这样的问题:

为什么会有这样的问题,则需要先理解cherry的排他机制
理解排它:
一言以蔽之,排他就是某个语法利用自己的“先发优势(如beforeMakeHtml、makeHtml)”把符合自己语法规则的内容先替换成占位符,再利用自己的“后发优势(afterMakeHtml)”将占位符替换回html内容
未完待续...