CodeMirror是一款在线的支持语法高亮的代码编辑器 github源码
几十个demo可供参考
已经应用CodeMirror的产品
<link rel="stylesheet" href="http://codemirror.net/lib/codemirror.css">
<script src="http://codemirror.net/lib/codemirror.js">script>
<script src="http://codemirror.net/mode/javascript/javascript.js">script>
<body>
<form>
<textarea id="myTextarea">textarea>
form>
<div id="code">div>
body>
推荐的使用方法
编辑器将会替换textarea
,和textarea平级
,自动把textarea
隐藏掉(display: none)
// 创建实例 这种方法在options 里写value属性是没有用的,自3.0版本改掉了
// 对于生成实例的步骤,react用户注意,要在真实dom生成之后再调用
const myTextarea = document.getElementById('myTextarea')
const editor = CodeMirror.fromTextArea(myTextarea, {
lineNumbers: true,
mode: 'javascript' // 设置mode 对应的也要这之前引入相应的js
});
编辑器将被追加到div里边
// 编辑器将被追加到div 里边
var myCodeMirror2 = CodeMirror(document.getElementById("code"), {
lineNumbers: true,
value: "function myScript(){return 100;}\n",
mode: "javascript"
});
编辑器将替换
元素
var myTextArea = document.getElementById("code");
var editor = CodeMirror(function(elt) {
myTextArea.parentNode.replaceChild(elt, myTextArea);
}, {
value: 'select * from table6666',
lineNumbers: true,
});
更多场景,就要通过options
配置 和 eitor
的方法去实现了
value: string | CodeMirror.Doc
编辑器的初始值(文本),可以是字符串或者CodeMirror文档对象
mode: string | object
通用的或者在CodeMirror
中使用的与mode
相关联的MIME,当不设置这个值的时候,会默认使用第一个载入的mode
定义文件。一般地,会使用关联的mime类型来设置这个值;除此之外,也可以使用一个带有name属性的对象来作为值(如:{name: “javascript”, json: true})
。可以通过访问CodeMirror.modes
和CodeMirror.mime
Modes`获取定义的mode和MIME
lineSeparator: string | null
明确指定编辑器使用的行分割符(换行符)。默认(值为null
)情况下,文档会被 CRLF
(以及单独的CR, LF)分割,单独的LF会在所有的输出中用作换行符(如:getValue
)。当指定了换行字符串,行就只会被指定的串分割。
theme: string
主题样式,主题对应的样式类名为.cm-s-[name]
,参考其他主题,也可以自定义主题样式
indentUnit: integer
缩进为多少个空格,默认为2
smartIndent: boolean
换行的时候是否使用模式提供的上下文相关的缩进(或只是缩进与之前的行对其), 默认true
tabSize: integer
一个tab有几个空格,默认是4
个
indentWithTabs: boolean
在缩进时,是否需要把 n*tab
宽度个空格替换成n
个tab
字符,默认为false
keyMap: string
配置快捷键图,编辑模式。默认defaul
,这是codemirror.js
本身定义的唯一键映射。其他选项参考key map
extraKeys: object
可以为编辑器绑定额外的键盘事件。
lineWrapping: boolean
当一行特别长的时候,是滚动还是换行。默认为false
(滚动)
lineNumbers: boolean
是否在编辑器左侧显示行号
firstLineNumber: interger
第一行的行号,默认为1
lineNumberFormatter: function(line: integer) → string
自定义行号
fixedGutter: boolean
是否在内容水平滚动的时候,行号也跟着滚动,(默认为true)
readOnly: boolean
编辑器是否可以编辑(默认false)
showCursorWhenSelecting: boolean
选中的时候,是否显示光标,默认false
lineWiseCopyCut: boolean
在没有选择时进行复制或剪切,将复制或剪切光标所在的行
undoDepth: integer
编辑器存储的撤消级别的最大数量,默认200
次
autofocus: boolean
自动获取焦点
dragDrop: boolean
是否启用拖放功能,就是把文件拖进来自动打开。
allowDropFileTypes: array
设置可以拖放的文件类型
editor.on('change', (instance, change) => {
// instance 指CodeMirror实例对象
// .....
do what you want
})
CodeMirror 提供了很多的方法,这些方法允许客户端对各种情况做出不同的行为,这些事件可以通过on
和off
来绑定和解除绑定处理函数
change (instance: CodeMirror, changeObj: object)
编辑器发生改变的时候触发(不建议在change
事件中做过多的操作,比如setState
,否则会导致卡顿)
beforeChange (instance: CodeMirror, changeObj: object)
在更改之前触发,可以选择修改更改的内容或者取消更改,changeObj
提供了cancel()
和update
的方法
cursorActivity (instance: CodeMirror)
当改变、光标移动、选择的时候触发
keyHandled (instance: CodeMirror, name: string, event: Event)
快捷键映射(key map)
中的快捷键被处理(handle)
后触发
inputRead (instance: CodeMirror, changeObj: object)
当用户输入,或者粘贴时触发,(和change
区别:撤销,恢复不会触发)
viewportChange (instance: CodeMirror, from: number, to: number)
每当编辑器的视图窗口发生更改(由于滚动,编辑或其他因素)时触发
gutterClick (instance: CodeMirror, line: integer, gutter: string, clickEvent: Event)
当编辑区域的行号被点击时触发,line
是从0
开始的, gutter
为行号的class
名
gutterContextMenu (instance: CodeMirror, line: integer, gutter: string, contextMenu: Event: Event)
编辑器行号(contextmenu)
右键出现上下午菜单的时候触发
focus (instance: CodeMirror, event: Event)
编辑器获取到焦点的时候触发
blur (instance: CodeMirror, event: Event)
失去焦点时触发
scroll (instance: CodeMirror)
滚动的时候触发
optionChange (instance: CodeMirror, option: string)
当配置项别改变的时候触发(setOption)
update (instance: CodeMirror)
当CodeMirror
更新其DOM
显示时触发
mousedown, dblclick, touchstart, contextmenu, keydown, keypress, keyup, cut, copy, paste, dragstart, dragenter, dragover, dragleave, drop (instance: CodeMirror, event: Event)
dom
的原生事件
editor.getValue() // 当前编辑器的content
getValue (?separator: string) → string
获取编辑器的内容,你可以传入一个特殊的字符串,去分割行,默认'\n'
。
setValue (content: string)
设置编辑器的内容,实例初始化的时候可以通过option:value
,或者写入textarea
里指定编辑器的内容。实例生成之后,通过这个方法去设置编辑器的内容。
getRange (from: {line, ch}, to: {line, ch}, ?separator: string) → string
获取编辑器指定位置的内容,可以传入特定的行分割符,line
第几行,ch
第几列,都是从0
开始
replaceRange (replacement: string, from: {line, ch}, to: {line, ch}, ?origin: string)
用给定的字符串替换或插入指定的地方
getLine (n: integer) → string
获取指定行的内容(0 开始)
lineCount () → integer
获取编辑器一共有多少行
getLineHandle (num: integer) → LineHandle
获取指定行的实例对象
getLineNumber (handle: LineHandle) → integer
根据行的实例,获取行的行号(0开始)
eachLine(f: (line: LineHandle)) 或者 (start: integer, end: integer, f: (line: LineHandle))
遍历整个文档,或者如果给出了开始和结束行号,则从开始到(不包括)结束的范围,并且为每一行调用f
,传递行句柄。这是访问一系列行处理程序比为每个行调用getLineHandle
更快的方法
getSelection (?lineSep: string) → string
获取选中的内容,可以传入一个行分割符默认'\n'
,返回的是字符串。当有有多个选中的区域,他们之间的链接就是这个分割符
getSelections (?lineSep: string) → array
获取多个选中的内容,返回的是一个数组,其他同上。
replaceSelection (replacement: string, ?select: string)
用给定的字符串替换选中的。可选的select
当传入'around'
时,替换完成后会自动选中新插入的文本,'start'
时,替换完后光标在最开始的位置
replaceSelections(replacements: array, ?select: string)
用数组中的字符替换选中的内容,替换的数组的长度应该和选中的数组的长度保持一致
getCursor (?start: string) → {line, ch}
返回光标所在的位置
listSelections () → array<{anchor, head}>
返回当前被选中的Range
实例
somethingSelected () → boolean
当选中任何内容,将会返回true
setSelection (anchor: {line, ch}, ?head: {line, ch}, ?options: object)
给定位置,设置选中的区域,如果head
没传,head
默认为anchor
。
options有以下选项:
scroll: boolean
是否将选中的区域滚动到可视范围。
origin: string
确定选择历史事件是否可以与前一个合并。具体:
Determines whether the selection history event may be merged with the previous one. When an origin starts with the character +, and the last recorded selection had the same origin and was similar (close in time, both collapsed or both non-collapsed), the new one will replace the old one. When it starts with *, it will always replace the previous event (if that had the same origin). Built-in motion uses the “+move” origin. User input uses the “+input” origin.
bias: number
确定选择端点落入原子范围内时应调整的方向(具体我也不清楚)
Determine the direction into which the selection endpoints should be adjusted when they fall inside an atomic range. Can be either -1 (backward) or 1 (forward). When not given, the bias will be based on the relative position of the old selection—the editor will try to move further away from that, to prevent getting stuck.
setCursor (pos: {line, ch}|number, ?ch: number, ?options: object)
设置光标位置。可以传参数 {line, ch}
对象,也可以将这两个值作为两个参数传入。将在给定位置用一个空的选择来替换所有的选择,options
同setSelection
的options
参数
setSelections (ranges: array<{anchor, head}>, ?primary: integer, ?options: object)
设置一组新的选择。给定数组中至少有一个选择。当primary
是一个数字,它决定哪个选择是主要的。如果没有给出,则主索引从前一个选择中获取,如果前一个选择的范围小于新的范围,则将其设置为最后一个范围。options
同setSelection
的options
参数
addSelection (anchor: {line, ch}, ?head: {line, ch})
添加一个新的选择到现有的选择,并使其成为主要的选择
extendSelection (from: {line, ch}, ?to: {line, ch}, ?options: object)
与setSelection
类似。区别在于,当按下shift键或设置了extending标志时(指的是设置为true),只会移动head的位置,anchor
会被留在当前位置。参数to
是可选的,传此参数用于确保区域(region
,比如单词或段落)能被完整选择。当前有多个选区时,会清除掉主选区。参数options
同setSelection
extendSelections (heads: array<{line, ch}>, ?options: object)
相当于一次执行所有选择的extendSelection
extendSelectionsBy(f: function(range: {anchor, head}) → {line, ch}), ?options: object)
将给定的函数应用于所有现有的选择,并调用extendSelections
hasFocus() → boolean
编辑器当前是否有焦点
// 映射Tab键以插入空格而不是制表符
editor.setOption("extraKeys", {
Tab: function(cm) {
var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
cm.replaceSelection(spaces);
}
});
setOption (option: string, value: any)
更改编辑器的配置。option
应该是一个选项的名称,value
应该是该选项的有效值
getOption (option: string) → any
检索此编辑器实例option
的值
setSize (width: number|string, height: number|string)
设置编辑器的宽高
scrollTo(x: number, y: number)
主动让编辑器滚动
getScrollInfo () → {left, top, width, height, clientWidth, clientHeight}
获取表示当前滚动位置,可滚动区域的大小以及可见区域的大小(减号滚动条)的{left,top,width,height,clientWidth,clientHeight}
getMode () → object
获取编辑器的模式对象。请注意,这不同于getOption(“mode”)
。当前返回的是模式实例化对象
isReadOnly() → boolean
返回编辑器当前是否允许编辑。
getWrapperElement () → Element
返回代表当前编辑器的DOM
CodeMirror.fromTextArea(textArea: TextAreaElement, ?config: object)
,这个方法创建的实例有以下几个方法:
save ()
将编辑器的内容复制到textarea
中,这样表单中的textarea的值就会和编辑器同步,以方便表单验证
toTextArea ()
删除编辑器,并恢复原始文本区(内容与编辑器的当前内容保持一致)
getTextArea () → TextAreaElement
返回实例所基于的textarea
CodeMirror
提供了很多的快捷键去完成编写 具体参考
CodeMirror
在mobx
、react
中的应用demo
场景:编辑器支持可编辑,代码高亮,代码提示
// code.js 此处codemirror是npm包
import {Component} from 'react'
import {observer} from 'mobx-react'
// 如果是安装包的形式,这样引入
import CodeMirror from 'codemirror'
import 'codemirror/lib/codemirror.css'
import 'codemirror/'
// 引入mode
import 'codemirror/mode/sql/sql'
// 引入代码提示
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/hint/show-hint'
// 上边两个是定义提示的前提,下边定义自动提示是哪种模式,此处为sql
import 'codemirror/addon/hint/sql-hint'
// 引入keymap
import 'codemirror/addon/comment/comment'
import 'codemirror/keymap/sublime'
@observer
export default class Code extends Component {
render() {
return (
)
}
componentDidMount() {
this.editor = CodeMirror.fromTextArea(this.codeDom, {
lineNumbers: true,
keyMap: 'sublime',
indentUnit: 4,
tabSize: 4,
mode: 'text/x-mysql',
showCursorWhenSelecting: true,
// 这是针对sql有自定义表和字段的,这样可以把自己的表和字段也放入提示里。如果数据是异步请求获取的,可以通过editor.setOption('hintOptions', data)
hintOptions: {
tables: {
table1: [ 'col_A', 'col_B', 'col_C' ],
table2: [ 'other_columns1', 'other_columns2' ],
},
}
})
// 将自动提示绑定到change事件上,这样输入的时候就可以看到联想的关键词
this.editor.on('change', (instance, change) => {
// 自动补全的时候,也会触发change事件,所有坐下判断,以免死循环,正则是为了不让空格,换行之类的也提示
// 通过change对象你可以自定义一些规则去判断是否提示
if (change.origin !== 'complete' && /\w|\./g.test(change.text[0])) {
instance.showHint()
}
})
}
// 获取编辑器的内容,以便提交
getTextareaCode = () => this.editor.getValue()
}
效果如下:
自动提示的时候,当只有一个匹配选项CodeMirror
会自动帮你选,这样会影响用户输入其他前几个字符一样的单词,导致无法编辑相应的词,所以应该让用户自己去选。改掉源码
show-hint.js
中的选中逻辑。
finishUpdate: function(data, first) {
if (this.data) CodeMirror.signal(this.data, "update");
var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
if (this.widget) this.widget.close();
if (data && this.data && isNewCompletion(this.data, data)) return;
this.data = data;
if (data && data.list.length) {
// 将这几行注释掉
// if (picked && data.list.length == 1) {
// this.pick(data, 0);
// } else {
this.widget = new Widget(this, data);
CodeMirror.signal(data, "shown");
// }
}
}
CodeMirror
提供了很多内置的API
,提高了可扩展性,可自定义mode
,可自定义hint
,等等,在此都不一一列举了。就整理这么多了,还有很多很多我没有涉及到的,欢迎补充,谢谢^_^
。