介绍
最近在开发Typecho主题Freewind的1.4版本,在开发过程中下载了一些比较优秀的Typecho
主题如Joe主题,然后发现主题作者自已对Typecho
的markdown
编辑器是做了增强的,添加了一些自定义的功能,于是就想着自己也对做一款符合Typecho主题Freewind的markdown
编辑器,于是查看了一些资源找到了一款开源产品codemirror,然后开始基于codemirror做款封装
Codemirror官方文档:传送门
Codemirror使用
准备
- 用如下命令下载codemirror核心组件包
npm install codemirror
import CodeMirror from 'codemirror'
创建Codemirror对象
我们可以使用如下方法创建一个cm(本文对codemirror的简称,下方同理)对象
let cm = CodeMirror.fromTextArea(dom, {}) // 两个参数:第一个参数为textarea的dom对象,第二个参数为cm的配置参数
我对我了解到的几个参数做下说明
参数名 | 说明 |
---|---|
mode | 语言模式,比如我们是markdown编辑器,我们就填写markdown,但要引入相关依赖 |
theme | 主题,除了默认主题外其它主题需要引入相关依赖 |
tabSize | tab的长度 |
lineNumbers | 显示行号 |
matchTags | 匹配标签,需要引入相关依赖 |
matchBrackets | 括号匹配,需要引入相关依赖 |
lineWiseCopyCut | 拷贝一行 |
indentWithTabs | 开启tab键 |
indentUnit | tab键长度 |
autoCloseTags | 自动闭合标签,需要引入相关依赖 |
autoCloseBrackets | 自动闭合括号,需要引入相关依赖 |
autofocus | 自动获取焦点 |
styleActiveLine | 高亮选中行,需要引入相关依赖 |
scrollPastEnd | 在编辑器底部插入一个编辑器同等高度的空白 |
showReplace | 是否显示replace |
extraKeys | 快捷键扩展,比较常用的ctrl+b加粗,ctrl+i 倾斜等 |
我构那家cm对象的代码
this.cm = CodeMirror.fromTextArea(document.getElementById(id), {
mode: 'markdown',
theme: 'material-palenight',
tabSize: 4,
lineNumbers: true, // 显示行号
matchTags: {bothTags: true}, // 匹配标签
matchBrackets: true, // 括号匹配
lineWiseCopyCut: true,
indentWithTabs: true,
indentUnit: 4,
lineWrapping: true,
autoCloseTags: true,
autoCloseBrackets: true,
autofocus: true,
foldGutter: true,
keyMap: 'sublime',
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
styleActiveLine: true,
scrollPastEnd: true, // 在编辑器底部插入一个编辑器同等高度的空白
continueComments: true,
lint: false,
selfContain: true,
showReplace: false, // 是否显示replace
highlightSelectionMatches: {
showToken: true,
annotateScrollbar: true,
},
hintOptions: {
completeSingle: false,
alignWithWord: false,
}
})
我在使用上述参数后引入的依赖
import CodeMirror from 'codemirror'
// 代码高亮
import 'codemirror/mode/markdown/markdown'
// 代码收缩
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter'
import 'codemirror/addon/fold/markdown-fold'
import 'codemirror/addon/fold/brace-fold'
// 标签匹配与编辑配置
import 'codemirror/addon/edit/closetag'
import 'codemirror/addon/edit/closebrackets'
import 'codemirror/addon/edit/matchtags'
import 'codemirror/addon/edit/matchbrackets'
import 'codemirror/addon/edit/continuelist'
import 'codemirror/addon/edit/trailingspace'
// 快捷键
import 'codemirror/keymap/sublime'
// 搜索
import 'codemirror/addon/search/search'
import 'codemirror/addon/dialog/dialog.css'
// 其它
import 'codemirror/addon/selection/active-line'
import 'codemirror/keymap/sublime'
//主题样式
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/material-palenight.css'
//全屏
import 'codemirror/addon/display/fullscreen'
import 'codemirror/addon/display/fullscreen.css'
扩展快捷键
我这边新加上几个markdown
常用快捷键
快捷键 | 说明 |
---|---|
Tab | 如果光标选中了任何值,整行缩进 ; 如果当前行光标左边的一个字符为空或者为tab或空格,进行缩进 |
Ctrl/Command+Enter | 判断开头是'> '还是'- '还是'1. '开头,则下行自动补全 |
Ctrl/Command+B | 加粗 |
Ctrl/Command+I | 倾斜 |
Ctrl/Command+D | 删除线 |
Ctrl/Command+. | 添加引用 |
我这边扩展快捷键的代码
extraKeys: {
Tab: (cm) => {
/**
* 处理策略
* 如果光标选中了任何值,整行缩进
* 如果当前光标所在编辑窗口为markdown,正常缩进
* 如果当前行光标左边的一个字符为空或者为tab或空格,进行缩进
*/
function indent() {
// const spaces = Array(cm.getOption('indentUnit') + 1).join(' ')
cm.replaceSelection('\t', 'end', '+input')
}
if (cm.somethingSelected()) {
// 光标选中文本
cm.indentSelection('add') // 整行缩进
} else {
const cursor = cm.getCursor() // 获取焦点
const line = cursor.line // 获取光标所在行数
const ch = cursor.ch // 获取光标位置
if (ch === 0 || cm.getOption('mode') === 'text/md-mix') {
indent() // 为markdown
} else {
const value = cm.getLine(line) // 获取当前行文本
const front = value[ch - 1] // 获取光标前一字符
switch (front) { // 为空格,tab或其他特定字符
case '\t':
case '<':
case ' ':
case "'":
case '/':
indent()
return void 0
}
if (cm.getOption('mode') === 'text/html') {
// 为html
front === '>' && indent()
try {
cm.execCommand('emmetExpandAbbreviation') // emmet扩展
} catch (err) {
console.error(err)
}
} else {
indent()
// cm.showHint()
}
}
}
},
[`${runKey}-Enter`]: (cm) => {
// 引用,无序,有序列表延伸
let matchStr = ''
// 判断开头是'> '还是'- '还是'1. '开头
if (cm.somethingSelected()) {
const selectContent = cm.listSelections()[0] // 第一个选中的文本
let {anchor, head} = selectContent
// 选中文本时,光标要么在内容前,要么在内容后,需要判断前后位置
head.line >= anchor.line && head.sticky === 'before' && ([head, anchor] = [anchor, head])
let {line: preLine, ch: prePos} = head
const selectVal = cm.getSelection()
let preStr = cm.getRange({line: preLine, ch: 0}, head)
let preBlank = ''
if (/^( |\t)+/.test(preStr)) {
preBlank = preStr.match(/^( |\t)+/)[0]
preStr = preStr.trimLeft()
}
if (/^> /.test(preStr)) {
// 以'> '开头
matchStr = '> '
prePos && (matchStr = `\n${preBlank}${matchStr}${selectVal}\n`) && ++preLine
cm.replaceSelection(matchStr)
cm.setCursor({line: preLine, ch: matchStr.length})
} else if (/^- /.test(preStr)) {
// 以'- '开头
matchStr = '- '
prePos && (matchStr = `\n${preBlank}${matchStr}${selectVal}\n`) && ++preLine
cm.replaceSelection(matchStr)
cm.setCursor({line: preLine, ch: matchStr.length})
} else if (/^\d+(\.) /.test(preStr)) {
let preNumber = 0
if (/^\d+(\.) /.test(preStr)) {
// 是否以'数字. '开头,找出前面的数字
preNumber = Number.parseInt(preStr.match(/^\d+/)[0])
}
matchStr = `\n${preBlank}${preNumber + 1}. ${selectVal}\n`
cm.replaceSelection(matchStr)
cm.setCursor({line: preLine + 1, ch: matchStr.length - 2})
}
} else {
const cursor = cm.getCursor()
let {line: curLine, ch: curPos} = cursor // 获取光标位置
let preStr = cm.getRange({line: curLine, ch: 0}, cursor)
let preBlank = ''
if (/^( |\t)+/.test(preStr)) {
// 有序列表标识前也许会有空格或tab缩进
preBlank = preStr.match(/^( |\t)+/)[0]
preStr = preStr.trimLeft()
}
if (/^> /.test(preStr)) {
// 以'> '开头
matchStr = '> '
curPos && (matchStr = `\n${preBlank}${matchStr}\n`) && ++curLine
cm.replaceSelection(matchStr)
cm.setCursor({line: curLine, ch: matchStr.length})
} else if (/^- /.test(preStr)) {
// 以'- '开头
matchStr = '- '
curPos && (matchStr = `\n${preBlank}${matchStr}\n`) && ++curLine
cm.replaceSelection(matchStr)
cm.setCursor({line: curLine, ch: matchStr.length})
} else if (/^\d+(\.) /.test(preStr)) {
// 以'数字. '开头
let preNumber = 0
if (/^\d+(\.) /.test(preStr)) {
// 是否以'数字. '开头,找出前面的数字
preNumber = Number.parseInt(preStr.match(/^\d+/)[0])
}
matchStr = `\n${preBlank}${preNumber + 1}. `
cm.replaceSelection(matchStr)
cm.setCursor({line: curLine + 1, ch: matchStr.length - 1})
}
}
cm.focus()
},
[`${runKey}-B`]: (cm) => {
// 加粗
editorTools.handleTextStyle(cm, '**')
},
[`${runKey}-I`]: (cm) => {
// 倾斜
editorTools.handleTextStyle(cm, '*')
},
[`${runKey}-D`]: (cm) => {
// 删除
editorTools.handleTextStyle(cm, '~~')
},
[`${runKey}-.`]: (cm) => {
editorTools.handleUnorderedList(cm, '>')
},
}
其中runkey
为你操作系统的cmd/ctrl
,获取代码为
const mac = CodeMirror.keyMap.default === CodeMirror.keyMap.macDefault
const runKey = mac ? 'Cmd' : 'Ctrl'
editorTools
为一个自定义工具包,我在网上找的,然后改了一下,具体代码如下:
/**
* Insert matching strings such as ~, **, ', etc. into both sides of the selected text to change the style
* For code, bold, italic, and hyphen styles
* 将匹配字符串如:~、**、`等插入选中文本两边以达到改变样式的效果
* 用于代码,粗体,斜体和中划线样式
* @param {Object} cm codemirror实例
* @param {String} matchStr 匹配字符串
*/
function handleTextStyle(cm, matchStr) {
/**
* 已经选中文本
* --- 获取选中的文本和前后光标位置
* --- 判断文本前后是否有匹配字符串
* --- 有匹配字符串
* ------ 删除匹配字符串
* --- 没有匹配字符串
* ------ 插入匹配字符串
* 没有选中文本
* --- 获取光标位置
* --- 判断光标前后是否有匹配字符串,处理如上
*/
const changePos = matchStr.length
let [preExist, aftExist] = [false, false]
if (cm.somethingSelected()) {
const selectInfo = cm.listSelections()[0]
let {head, anchor} = judgePreOrAft(selectInfo)
let {line: preLine, ch: prePos} = head
let {line: aftLine, ch: aftPos} = anchor
cm.getRange({line: preLine, ch: prePos - changePos}, head) === matchStr && (preExist = true)
cm.getRange(anchor, {line: aftLine, ch: aftPos + changePos}) === matchStr && (aftExist = true)
let preStr = preExist ? '' : matchStr
let aftStr = aftExist ? '' : matchStr
prePos -= preExist ? changePos : 0
aftPos += aftExist ? changePos : 0
const selectStr = cm.getSelection()
cm.replaceRange(`${preStr}${selectStr}${aftStr}`, {line: preLine, ch: prePos}, {line: aftLine, ch: aftPos})
} else {
const cursor = cm.getCursor()
const {line: curLine, ch: curPos} = cursor
cm.getRange({line: curLine, ch: curPos - changePos}, cursor) === matchStr && (preExist = true)
cm.getRange(cursor, {line: curLine, ch: curPos + changePos}) === matchStr && (aftExist = true)
if (preExist && aftExist) {
cm.replaceRange('', cursor, {line: curLine, ch: curPos + changePos})
cm.replaceRange('', {line: curLine, ch: curPos - changePos}, cursor)
cm.setCursor({line: curLine, ch: curPos - changePos})
} else if (!preExist && !aftExist) {
cm.replaceSelection(matchStr + matchStr)
cm.setCursor({line: curLine, ch: curPos + changePos})
} else {
cm.replaceRange()
}
}
cm.focus()
}
/**
* Adding an Ordered List
* 添加有序列表
* @param {Object} cm codemirror实例
*/
function handleOrderList(cm) {
/**
* 已经选中文本
* --- 获取选中的文本和前后光标位置
* --- 选中了多行文本
* ------ 在每行前加上数字列表
* --- 选中单行文本
* ------ 检查文本前是否含有tab缩进
* ------ 检查文本是否已‘数字.’开头
* ------ 在开头加上数字列表
* 没有选中文本
* --- 获取光标位置
* --- 检查文本前是否含有tab缩进
* --- 检查文本是否已‘数字.’开头
* --- 在开头加上数字列表
*/
if (cm.somethingSelected()) {
const selectInfo = cm.listSelections()[0]
let {head, anchor} = judgePreOrAft(selectInfo)
let preLine = head.line
let aftLine = anchor.line
if (preLine !== aftLine) {
let preNumber = 0
let pos = 0
for (let i = preLine; i <= aftLine; i++) {
cm.setCursor({line: i, ch: 0})
const replaceStr = `${++preNumber}. `
cm.replaceSelection(replaceStr)
if (i === aftLine) {
pos += (replaceStr + getLine(i)).length
}
}
cm.setCursor({line: aftLine, ch: pos})
} else {
const selectVal = cm.getSelection()
let preStr = cm.getRange({line: preLine, ch: 0}, head)
let preNumber = 0
let preBlank = ''
if (/^([ \t])+/.test(preStr)) {
preBlank = preStr.match(/^([ \t])+/)[0]
preStr = preStr.trimLeft()
}
if (/^\d+(\.) /.test(preStr)) {
preNumber = Number.parseInt(preStr.match(/^\d+/)[0])
}
let replaceStr = `\n${preBlank}${preNumber + 1}. ${selectVal}\n`
cm.replaceSelection(replaceStr)
cm.setCursor({line: preLine + 1, ch: replaceStr.length})
}
} else {
const cursor = cm.getCursor()
const curLine = cursor.line
let preStr = cm.getRange({line: curLine, ch: 0}, cursor)
let preNumber = 0
let preBlank = ''
if (/^([ \t])+/.test(preStr)) {
preBlank = preStr.match(/^([ \t])+/)[0]
preStr = preStr.trimLeft()
}
if (/^\d+(\.) /.test(preStr)) {
preNumber = Number.parseInt(preStr.match(/^\d+/)[0])
}
let replaceStr = `\n${preBlank}${preNumber + 1}. `
cm.replaceSelection(replaceStr)
cm.setCursor({line: curLine + 1, ch: replaceStr.length - 1})
}
cm.focus()
}
/**
* Add references and unordered lists
* 添加引用和无序列表
* @param {Object} cm codemirror实例
* @param {String} matchStr 匹配字符串
*/
function handleUnorderedList(cm, matchStr) {
/**
* 已经选中文本
* --- 获取选中的文本和前后光标位置
* --- 选中了多行文本
* ------ 在每行前加上匹配字符
* --- 选中单行文本
* ------ 检测开头是否有匹配的字符串,有就将其删除,否则插入匹配字符
* 没有选中文本
* --- 获取光标位置
* --- 检查文本前是否含有tab缩进
* --- 检查文本是否已‘数字.’开头
* --- 插入匹配字符
*/
if (cm.somethingSelected()) {
const selectInfo = cm.listSelections()[0]
let {head, anchor} = judgePreOrAft(selectInfo)
let preLine = head.line
let aftLine = anchor.line
if (preLine !== aftLine) {
let pos = matchStr.length
for (let i = preLine; i <= aftLine; i++) {
cm.setCursor({line: i, ch: 0})
cm.replaceSelection(matchStr)
i === aftLine && (pos += cm.getLine(i).length)
}
cm.setCursor({line: aftLine, ch: pos})
} else {
const preStr = cm.getRange({line: preLine, ch: 0}, head)
if (preStr === matchStr) {
cm.replaceRange('', {line: preLine, ch: 0}, head)
} else {
const selectVal = cm.getSelection()
let replaceStr = `\n${matchStr}${selectVal}\n`
cm.replaceSelection(replaceStr)
cm.setCursor({line: preLine + 2, ch: (matchStr + selectVal).length})
}
}
} else {
const cursor = cm.getCursor()
let {line: curLine, ch: curPos} = cursor
let preStr = cm.getRange({line: curLine, ch: 0}, cursor)
let preBlank = ''
if (/^([ \t])+/.test(preStr)) {
preBlank = preStr.match(/^([ \t])+/)[0]
}
curPos && (matchStr = `\n${preBlank}${matchStr}`) && ++curLine
cm.replaceSelection(matchStr)
cm.setCursor({line: curLine, ch: matchStr.length - 1})
}
cm.focus()
}
/**
* Add a horizontal or dividing line
* 添加横线/分割线
* @param {Object} cm codemirror实例
*/
function handleLine(cm) {
/**
* 已经选中文本
* --- 获取选中的文本和前后光标位置
* --- 插入横线
* 没有选中文本
* --- 获取光标位置
* --- 插入横线
*/
if (cm.somethingSelected()) {
const selectInfo = cm.listSelections()[0]
let head = judgePreOrAft(selectInfo).head
let {line: preLine, ch: prePos} = head
let replaceStr = '\n\n---\n'
cm.replaceSelection(replaceStr)
cm.setCursor({line: preLine, ch: prePos})
} else {
const cursor = cm.getCursor()
let {line: curLine, ch: curPos} = cursor
let replaceStr = curPos ? '\n\n---\n\n' : '\n---\n\n'
curLine += curPos ? 4 : 3
cm.replaceSelection(replaceStr)
cm.setCursor({line: curLine, ch: 0})
}
cm.focus()
}
/**
* Add headings at the heading levels H1,H2,H3,H4,H5,H6
* 根据标题级别H1,H2,H3,H4,H5,H6,添加标题
* @param {Object} cm codemirror实例
* @param {Number} level 级别
*/
function handleTitle(cm, level) {
/**
* 已经选中文本
* --- 获取选中的文本和前后光标位置
* --- 如果选中了文字,并且起始位置为 0
* ------ 插入标题
* --- 选中了文字但起始不为0,判断前面是否已经有标题了
* ------ 如果前面是以多个#开头并以一个空格结尾
* --------- 删除标题
* ------ 起始不为0,且前面也不存在标题的标识
* --------- 插入标题
* 没有选中文本
*/
let preAppend = '#'
for (let i = 0; i < level - 1; i++) {
preAppend += '#'
}
preAppend += ' '
if (cm.somethingSelected()) {
const selectInfo = cm.listSelections()[0]
const selectVal = cm.getSelection()
let {head, anchor} = judgePreOrAft(selectInfo)
let {line: preLine, ch: prePos} = head
let aftLine = anchor.line
if (preLine !== aftLine) return void 0
if (!prePos) {
cm.replaceRange(`${preAppend}${selectVal}\n`, head, anchor)
// cm.setCursor({ line: preLine, ch: 0 })
// cm.replaceSelection(`${preAppend}`)
// cm.setCursor({ line: preLine, ch: (preAppend + selectVal).length })
// cm.replaceSelection('\n')
} else {
const curLineVal = cm.getLine(preLine)
const matchStr = curLineVal.substr(0, prePos)
if (/^(#*) $/.test(matchStr)) {
cm.replaceRange('', {line: preLine, ch: 0}, head)
} else {
cm.replaceSelection(`\n${preAppend}${selectVal}\n`)
}
}
} else {
const cursor = cm.getCursor()
let {line: curLine, ch: curPos} = cursor // 获取光标位置
const curLineVal = cm.getLine(curLine) // 当前行内容
curPos && (preAppend = '\n\n' + preAppend)
cm.setCursor({line: curLine + 1, ch: 0})
cm.replaceSelection('\n')
cm.setCursor(cursor)
cm.replaceSelection(preAppend)
cm.setCursor({line: curLine, ch: curPos})
curLine += curPos ? 3 : 1
if (curPos === curLineVal.length) {
curLine -= 1
curPos = preAppend.length
} else {
curPos = 0
}
cm.setCursor({line: curLine, ch: curPos})
}
cm.focus()
}
/**
* Insert image or normal link
* 插入图片或普通链接
* @param {Object} cm codemirror实例
* @param {Boolean} isPicture 是否为图片链接
*/
function handleLink(cm, tx = "", url = "", isPicture = false) {
/**
* 已经选中文本
* --- 获取选中的文本和前后光标位置
* --- 选中多行文本
* ------ 退出
* --- 选中单行文本
* ------ 链接是否满足格式要求
* ------ 是否为图片链接
* ------ 插入链接格式
* 没有选中文本
* --- 是否为图片链接
* --- 插入链接格式
*/
if (cm.somethingSelected()) {
const selectInfo = cm.listSelections()[0]
// const selectVal = cm.getSelection()
let {head, anchor} = judgePreOrAft(selectInfo)
let {line: preLine, ch: prePos} = head
let aftLine = anchor.line
if (preLine !== aftLine) return void 0
// const isLinkStr = regExpList.httpUrl.test(selectVal)
let replaceStr = `[${tx}](${url})`
prePos += tx.length + 3
if (isPicture) {
prePos += 1
replaceStr = '!' + replaceStr
}
cm.replaceSelection(replaceStr)
cm.setCursor({line: preLine, ch: prePos})
} else {
const cursor = cm.getCursor()
let {line: curLine, ch: curPos} = cursor
let replaceStr = '[]()'
curPos += 3
isPicture && (replaceStr = '!' + replaceStr) && (curPos += 1)
cm.replaceSelection(replaceStr)
cm.setCursor({line: curLine, ch: curPos})
}
cm.focus()
}
/**
* Determine whether the cursor of the selected text is at the beginning or the end of the text
* 判断选中文本的光标是在文本开头还是结尾
* @param {Object} selectInfo
* @returns {Object}
*/
function judgePreOrAft(selectInfo) {
let {head, anchor} = selectInfo
;(head.line > anchor.line || (head.line === anchor.line && head.ch > anchor.ch)) && ([head, anchor] = [anchor, head])
return {head, anchor}
}
export default {
handleTextStyle,
handleOrderList,
handleUnorderedList,
handleLine,
handleTitle,
handleLink,
}
事件
这里有一些事件可以回调,回调方法如下
this.cm.on('事件名称', (e) => {
// todo 具体代码
console.log(e)
})
- changes:每次编辑器内容更改时触发
- beforeChange:事件在更改生效前触发
- cursorActivity:当光标或选中(内容)发生变化,或者编辑器的内容发生了更改的时候触发。
- keyHandled:快捷键映射(key map)中的快捷键被处理(handle)后触发
- inputRead:当用户输入或粘贴时编辑器时触发。
- electrictInput:收到指定的electrict输入时触发
- beforeSelectionChange:此事件在选中内容变化前触发
- viewportChange:编辑器的视口( view port )改变(滚动,编辑或其它动作)时触发
- gutterClick:编辑器的gutter(行号区域)点击时触发
- focus:编辑器收到焦点时触发
- blur:编辑器失去焦点时触发
- scroll:编辑器滚动条滚动时触发
比如我这里使用了两个使用,代码如下
this.cm.on('scroll', () => {
// 编辑器滚动条滚动,预览窗口也根着滚动
if (this.watch) {
let scroll = this.cm.getScrollInfo()
let rate = scroll['top'] / scroll['height']
this.viewer.scrollTop(this.viewer[0].scrollHeight * rate)
}
})
this.cm.on('change', (e) => {
// 保存内容到textarea
e.save()
// 这是我自定义的方法,生成html到预览窗口
this.md2html()
})
md2html(watch = false) {
let md = this.cm.getValue()
if (watch || (md !== this.lastmd)) {
this.lastmd = md
let ht = marked.parse(md)
if (this.settings['parse']) {
ht = this.settings.parse(ht)
}
if (watch || (ht !== this.viewer.html() && this.watch)) {
this.viewer.html(ht)
this.viewer.scrollTop(this.viewer[0].scrollHeight)
}
}
}
常用API
- cm.setValue(“Hello Kitty”):设置编辑器内容
- cm.getValue():获取编辑器内容
- cm.getLine(n):获取第n行的内容
- cm.lineCount():获取当前行数
- cm.lastLine():获取最后一行的行号
- cm.isClean():boolean类型判断编译器是否是clean的
- cm.getSelection():获取选中内容
- cm.getSelections():返回array类型选中内容
- cm.replaceSelection(“替换后的内容”):替换选中的内容
- cm.getCursor():获取光标位置,返回{line,char}
- cm.setCursor({line,char}):设置光标位置
- cm.setOption("",""):设置编译器属性
- cm.getOption(""):获取编译器属性
- cm.addKeyMap("",""):添加key-map键值,该键值具有比原来键值更高的优先级
- cm.removeKeyMap(""):移除key-map
- cm.addOverlay(""):Enable a highlighting overlay…没试出效果
- cm.removeOverlay(""):移除Overlay
- cm.setSize(width,height):设置编译器大小
- cm.scrollTo(x,y):设置scroll到position位置
- cm.refresh():刷新编辑器
- cm.execCommand(“命令”):执行命令
大家看上面的工具类也可以发现我这边用的比较多的是
- cm.getSelections():返回array类型选中内容
- cm.replaceSelection(“替换后的内容”):替换选中的内容
- cm.getCursor():获取光标位置,返回{line,char}
- cm.setCursor({line,char}):设置光标位置
所有代码
Fw-editor: 传送门
功能预览
预览地址: 传送门
常瑞 游客 2021-12-27 20:58 回复
想我这种学渣就看不懂