在线代码编辑器 CodeMirror 使用简介

在线代码编辑器 CodeMirror 使用简介


CodeMirror是一款在线的支持语法高亮的代码编辑器 github源码

特点:

  • 支持100多种语言
  • 支持多语言混合
  • 代码自动提示
  • 代码折叠
  • 键盘绑定事件
  • 编辑模式 Vim,Emacs和Sublime Text
  • 很多的方法和插件…

几十个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的方法去实现了


常用Configuration options 配置介绍
  • value: string | CodeMirror.Doc

    编辑器的初始值(文本),可以是字符串或者CodeMirror文档对象

  • mode: string | object

    通用的或者在CodeMirror中使用的与mode相关联的MIME,当不设置这个值的时候,会默认使用第一个载入的mode定义文件。一般地,会使用关联的mime类型来设置这个值;除此之外,也可以使用一个带有name属性的对象来作为值(如:{name: “javascript”, json: true})。可以通过访问CodeMirror.modesCodeMirror.mimeModes`获取定义的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宽度个空格替换成ntab字符,默认为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

    设置可以拖放的文件类型


常用的可绑定的事件 Events
    editor.on('change', (instance, change) => {
        // instance  指CodeMirror实例对象
        // .....
        do what you want
    })



CodeMirror 提供了很多的方法,这些方法允许客户端对各种情况做出不同的行为,这些事件可以通过onoff来绑定和解除绑定处理函数

  • 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的原生事件


CodeMirror API
文档操作

    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}对象,也可以将这两个值作为两个参数传入。将在给定位置用一个空的选择来替换所有的选择,optionssetSelectionoptions参数

  • setSelections (ranges: array<{anchor, head}>, ?primary: integer, ?options: object)

    设置一组新的选择。给定数组中至少有一个选择。当primary是一个数字,它决定哪个选择是主要的。如果没有给出,则主索引从前一个选择中获取,如果前一个选择的范围小于新的范围,则将其设置为最后一个范围。optionssetSelectionoptions参数

  • addSelection (anchor: {line, ch}, ?head: {line, ch})

    添加一个新的选择到现有的选择,并使其成为主要的选择

  • extendSelection (from: {line, ch}, ?to: {line, ch}, ?options: object)

    setSelection类似。区别在于,当按下shift键或设置了extending标志时(指的是设置为true),只会移动head的位置,anchor会被留在当前位置。参数to是可选的,传此参数用于确保区域(region,比如单词或段落)能被完整选择。当前有多个选区时,会清除掉主选区。参数optionssetSelection

  • 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 提供了很多的快捷键去完成编写 具体参考


实际应用实例

  • CodeMirrormobxreact中的应用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,等等,在此都不一一列举了。就整理这么多了,还有很多很多我没有涉及到的,欢迎补充,谢谢 ^_^

你可能感兴趣的:(others)