前几天突然想给自己的在线编译器加一个Markdown编辑功能,于是花了两三天敲敲打打初步实现了这个功能。
一个Markdown编辑器需要有如下常用功能:粗体
斜体
中划线
标题
链接
图片
引用
代码
有序列表
无序列表
横线
看上去想实现这些功能有点复杂,但是Codemirror提供了很多API可以更方便地修改编辑内容。CodeMirrorcodemirror.net
在阐述我是如何实现这些功能前,我先将实现时用到的API列出来。cm.somethingSelected()
是否选中编辑器内的任何文本。cm.listSelections()
选中的文本信息。cm.getRange(from: {line, ch}, to: {line, ch}, ?separator: string)
在编辑器中的给定点之间获取文本。cm.replaceRange(replacement: string, from: {line, ch}, to: {line, ch}, ?origin: string)
用replacement替换给定点之间的文本 。cm.setCursor(pos: {line, ch}|number, ?ch: number, ?options: object)
设置光标位置。cm.getCursor(?start: string)
获取光标位置 。cm.setSelection(anchor: {line, ch}, ?head: {line, ch}, ?options: object)
设置一个选择范围。cm.getLine(n: integer)
获取某行文本内容。
上面的API中,cm为Codemirror实例,也就是编辑器实例。line为行数,ch为列数(该行第几个字符)。
功能实现
首先是粗体,斜体,中划线和代码,这四个功能实现的方法是相同的。
当用户触发添加粗体、斜体、中划线或代码事件时,流程如下:
如上图所示,先来说说光标没选中文本时的处理:使用cm.getCursor()找到光标位置
使用cm.getRange()判断前后是否有匹配字符串(匹配字符串代表粗体、斜体、中划线或和代码的字符串:**、*、~~和'``') 。前面或后面有匹配字符串使用cm.replaceRange()清除匹配字符串
前面或后面没有匹配字符串使用cm.replaceSelection()添加匹配字符串
具体代码和注释如下:
const changePos = matchStr.length
let preAlready = false, aftAlready = false // 前后是否已经有相应样式标识,如**,`,~等 const cursor = cm.getCursor()
const { line: curLine, ch: curPos } = cursor // 获取光标位置 // 判断前后是否有matchStr cm.getRange({ line: curLine, ch: curPos - changePos }, cursor) ===
matchStr && (preAlready = true)
cm.getRange(cursor, { line: curLine, ch: curPos + changePos }) ===
matchStr && (aftAlready = true)
// 去除前后的matchStr if (aftAlready && preAlready) {
cm.replaceRange('', cursor, { line: curLine, ch: curPos + changePos })
<