Kaynağa Gözat

first commit

ZZMoore 2 yıl önce
işleme
aca987e0c5
100 değiştirilmiş dosya ile 32478 ekleme ve 0 silme
  1. BIN
      http.exe
  2. 164 0
      index.html
  3. BIN
      libs/banner.png
  4. 225 0
      libs/codemirror/addon/comment/comment.js
  5. 115 0
      libs/codemirror/addon/comment/continuecomment.js
  6. 32 0
      libs/codemirror/addon/dialog/dialog.css
  7. 178 0
      libs/codemirror/addon/dialog/dialog.js
  8. 47 0
      libs/codemirror/addon/display/autorefresh.js
  9. 6 0
      libs/codemirror/addon/display/fullscreen.css
  10. 43 0
      libs/codemirror/addon/display/fullscreen.js
  11. 141 0
      libs/codemirror/addon/display/panel.js
  12. 86 0
      libs/codemirror/addon/display/placeholder.js
  13. 54 0
      libs/codemirror/addon/display/rulers.js
  14. 201 0
      libs/codemirror/addon/edit/closebrackets.js
  15. 218 0
      libs/codemirror/addon/edit/closetag.js
  16. 122 0
      libs/codemirror/addon/edit/continuelist.js
  17. 160 0
      libs/codemirror/addon/edit/matchbrackets.js
  18. 68 0
      libs/codemirror/addon/edit/matchtags.js
  19. 31 0
      libs/codemirror/addon/edit/trailingspace.js
  20. 149 0
      libs/codemirror/addon/fold/brace-fold.js
  21. 77 0
      libs/codemirror/addon/fold/comment-fold.js
  22. 159 0
      libs/codemirror/addon/fold/foldcode.js
  23. 20 0
      libs/codemirror/addon/fold/foldgutter.css
  24. 170 0
      libs/codemirror/addon/fold/foldgutter.js
  25. 49 0
      libs/codemirror/addon/fold/indent-fold.js
  26. 52 0
      libs/codemirror/addon/fold/markdown-fold.js
  27. 232 0
      libs/codemirror/addon/fold/xml-fold.js
  28. 49 0
      libs/codemirror/addon/hint/anyword-hint.js
  29. 101 0
      libs/codemirror/addon/hint/css-hint.js
  30. 459 0
      libs/codemirror/addon/hint/html-hint.js
  31. 160 0
      libs/codemirror/addon/hint/javascript-hint.js
  32. 36 0
      libs/codemirror/addon/hint/show-hint.css
  33. 602 0
      libs/codemirror/addon/hint/show-hint.js
  34. 298 0
      libs/codemirror/addon/hint/sql-hint.js
  35. 135 0
      libs/codemirror/addon/hint/xml-hint.js
  36. 42 0
      libs/codemirror/addon/lint/coffeescript-lint.js
  37. 46 0
      libs/codemirror/addon/lint/css-lint.js
  38. 64 0
      libs/codemirror/addon/lint/html-lint.js
  39. 70 0
      libs/codemirror/addon/lint/javascript-lint.js
  40. 40 0
      libs/codemirror/addon/lint/json-lint.js
  41. 79 0
      libs/codemirror/addon/lint/lint.css
  42. 321 0
      libs/codemirror/addon/lint/lint.js
  43. 43 0
      libs/codemirror/addon/lint/yaml-lint.js
  44. 119 0
      libs/codemirror/addon/merge/merge.css
  45. 1082 0
      libs/codemirror/addon/merge/merge.js
  46. 81 0
      libs/codemirror/addon/mode/loadmode.js
  47. 137 0
      libs/codemirror/addon/mode/multiplex.js
  48. 41 0
      libs/codemirror/addon/mode/multiplex_test.js
  49. 93 0
      libs/codemirror/addon/mode/overlay.js
  50. 212 0
      libs/codemirror/addon/mode/simple.js
  51. 42 0
      libs/codemirror/addon/runmode/colorize.js
  52. 423 0
      libs/codemirror/addon/runmode/runmode-standalone.js
  53. 81 0
      libs/codemirror/addon/runmode/runmode.js
  54. 419 0
      libs/codemirror/addon/runmode/runmode.node.js
  55. 138 0
      libs/codemirror/addon/scroll/annotatescrollbar.js
  56. 49 0
      libs/codemirror/addon/scroll/scrollpastend.js
  57. 66 0
      libs/codemirror/addon/scroll/simplescrollbars.css
  58. 150 0
      libs/codemirror/addon/scroll/simplescrollbars.js
  59. 60 0
      libs/codemirror/addon/search/jump-to-line.js
  60. 170 0
      libs/codemirror/addon/search/match-highlighter.js
  61. 8 0
      libs/codemirror/addon/search/matchesonscrollbar.css
  62. 107 0
      libs/codemirror/addon/search/matchesonscrollbar.js
  63. 351 0
      libs/codemirror/addon/search/search.js
  64. 314 0
      libs/codemirror/addon/search/searchcursor.js
  65. 72 0
      libs/codemirror/addon/selection/active-line.js
  66. 125 0
      libs/codemirror/addon/selection/mark-selection.js
  67. 104 0
      libs/codemirror/addon/selection/selection-pointer.js
  68. 87 0
      libs/codemirror/addon/tern/tern.css
  69. 798 0
      libs/codemirror/addon/tern/tern.js
  70. 52 0
      libs/codemirror/addon/tern/worker.js
  71. 168 0
      libs/codemirror/addon/wrap/hardwrap.js
  72. 344 0
      libs/codemirror/codemirror.css
  73. 13058 0
      libs/codemirror/codemirror.js
  74. 175 0
      libs/codemirror/mode/apl/apl.js
  75. 72 0
      libs/codemirror/mode/apl/index.html
  76. 76 0
      libs/codemirror/mode/asciiarmor/asciiarmor.js
  77. 46 0
      libs/codemirror/mode/asciiarmor/index.html
  78. 203 0
      libs/codemirror/mode/asn.1/asn.1.js
  79. 78 0
      libs/codemirror/mode/asn.1/index.html
  80. 394 0
      libs/codemirror/mode/asterisk/asterisk.js
  81. 155 0
      libs/codemirror/mode/asterisk/index.html
  82. 77 0
      libs/codemirror/mode/brainfuck/brainfuck.js
  83. 85 0
      libs/codemirror/mode/brainfuck/index.html
  84. 943 0
      libs/codemirror/mode/clike/clike.js
  85. 380 0
      libs/codemirror/mode/clike/index.html
  86. 767 0
      libs/codemirror/mode/clike/scala.html
  87. 155 0
      libs/codemirror/mode/clike/test.js
  88. 880 0
      libs/codemirror/mode/clojure/clojure.js
  89. 95 0
      libs/codemirror/mode/clojure/index.html
  90. 999 0
      libs/codemirror/mode/clojure/test.js
  91. 95 0
      libs/codemirror/mode/cmake/cmake.js
  92. 129 0
      libs/codemirror/mode/cmake/index.html
  93. 266 0
      libs/codemirror/mode/cobol/cobol.js
  94. 210 0
      libs/codemirror/mode/cobol/index.html
  95. 348 0
      libs/codemirror/mode/coffeescript/coffeescript.js
  96. 740 0
      libs/codemirror/mode/coffeescript/index.html
  97. 156 0
      libs/codemirror/mode/commonlisp/commonlisp.js
  98. 177 0
      libs/codemirror/mode/commonlisp/index.html
  99. 482 0
      libs/codemirror/mode/crystal/crystal.js
  100. 0 0
      libs/codemirror/mode/crystal/index.html

BIN
http.exe


+ 164 - 0
index.html

@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="UTF-8" />
+        <title>四维看看SDK·开发文档</title>
+        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+        <meta name="description" content="Description" />
+        <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
+        <!-- <link rel="stylesheet" href="./libs/npm/docsify@4/lib/themes/vue.css"> -->
+        <link rel="stylesheet" href="./libs/npm/docsify-themeable@0/dist/css/theme-simple.css" />
+        <link rel="stylesheet" href="./libs/doc.css" />
+        <link rel="stylesheet" href="./libs/codemirror/codemirror.css" />
+        <script src="./libs/codemirror/codemirror.js"></script>
+        <script src="./libs/codemirror/mode/xml/xml.js"></script>
+        <script src="./libs/codemirror/mode/javascript/javascript.js"></script>
+        <script src="./libs/codemirror/mode/css/css.js"></script>
+        <script src="./libs/codemirror/mode/htmlmixed/htmlmixed.js"></script>
+        <script src="./libs/codemirror/addon/edit/matchbrackets.js"></script>
+    </head>
+    <body>
+        <nav></nav>
+        <div id="app"></div>
+        <script>
+            window.$docsify = {
+                //repo: 'https://github.com/4Dage-Developer/4dage-sdk',
+                name: '4DKanKan SDK Docs',
+                coverpage: ['/', '/zh-cn/'],
+                loadSidebar: true,
+                loadNavbar: '_navbar.md',
+                subMaxLevel: 4,
+                mergeNavbar: true,
+                onlyCover: false,
+                search: 'auto',
+                search: {
+                    maxAge: 86400000,
+                    paths: '/',
+                    placeholder: '搜索',
+                    noData: 'No Results',
+                },
+                markdown: {
+                    renderer: {
+                        code: function (code, lang) {
+                            if (/^\s*\/\*\s*online-demo\s*\*\//.test(code)) {
+                                if (code) {
+                                    code = code.replace(/^\s*\/\*\s*online-demo\s*\*\//, '')
+                                }
+                                return `
+                                    <div class="kankan-online-demo">
+                                        <iframe class="kankan-online-demo-iframe"></iframe>
+                                        <div class="kankan-online-demo-code">
+                                            <div class="kankan-online-demo-codebar">
+                                                <span>源代码编辑器</span>
+                                                <div>
+                                                    <div data-copy>复制</div>
+                                                    <div data-run>运行</div>
+                                                    <div style="display:none">还原</div>
+                                                </div>
+                                            </div>
+                                            <textarea class="kankan-online-demo-editor">${code}</textarea>
+                                        </div>
+                                    </div>
+                                `
+                            }
+                            return this.origin.code.apply(this, arguments)
+                        },
+                    },
+                },
+                plugins: [
+                    function (hook, vm) {
+                        hook.init(function () {
+                            // 初始化完成后调用,只调用一次,没有参数。
+                            // window.$docsify.markdown = window.$docsify.markdown || {}
+                            // window.$docsify.markdown.renderer = window.$docsify.markdown.renderer || {}
+                            // window.$docsify.markdown.renderer.code = exec(window.$docsify.markdown.renderer.code)
+                        })
+
+                        hook.beforeEach(function (content) {
+                            // 每次开始解析 Markdown 内容时调用
+
+                          
+
+                            return content
+                        })
+
+                        hook.afterEach(function (html, next) {
+                            // 解析成 html 后调用。
+                            // beforeEach 和 afterEach 支持处理异步逻辑
+                            // ...
+                            // 异步处理完成后调用 next(html) 返回结果
+                            next(html)
+
+                            var demos = document.querySelectorAll('.kankan-online-demo')
+                            if (demos) {
+                                demos.forEach(el => {
+                                    var iframe = el.querySelector('iframe')
+                                    var editor = CodeMirror.fromTextArea(el.querySelector('textarea'), {
+                                        lineNumbers: true,
+                                        mode: 'text/html',
+                                        matchBrackets: true,
+                                    })
+                                    editor.lineAtHeight(0)
+
+                                    var iframeDoc = iframe.contentDocument || iframe.contentWindow.document
+                                    iframeDoc.open()
+                                    iframeDoc.write(editor.getValue())
+                                    iframeDoc.close()
+                                   
+                                    el.querySelector('[data-run]').addEventListener(
+                                        'click',
+                                        (function (iframe, editor) {
+                                            return function () {
+                                                var doc = iframe.contentDocument || iframe.contentWindow.document
+                                                doc.open()
+                                                doc.write(editor.getValue())
+                                                doc.close()
+                                            }
+                                        })(iframe, editor)
+                                    )
+                                    el.querySelector('[data-copy]').addEventListener(
+                                        'click',
+                                        (function (iframe, editor) {
+                                            return function () {
+                                                navigator.clipboard.writeText(editor.getValue());
+                                                alert('复制成功')
+                                            }
+                                        })(iframe, editor)
+                                    )
+                                })
+                            }
+                        })
+
+                        hook.doneEach(function () {
+                            // 每次路由切换时数据全部加载完成后调用,没有参数。
+                            // ...
+                            if (document.querySelectorAll('.kankan-online-demo').length) {
+                                document.querySelector('section.content').classList.add('full')
+                            } else {
+                                document.querySelector('section.content').classList.remove('full')
+                            }
+                        })
+
+                        hook.mounted(function () {
+                            // 初始化并第一次加载完成数据后调用,只触发一次,没有参数。
+                        })
+
+                        hook.ready(function () {
+                            // 初始化并第一次加载完成数据后调用,没有参数。
+                        })
+                    },
+                ],
+            }
+        </script>
+        <!-- Docsify v4 -->
+        <script src="./libs/example/sync.js"></script>
+        <script src="./libs/npm/docsify.min.js"></script>
+        <script src="./libs/npm/docsify/lib/plugins/emoji.min.js"></script>
+        <script src="./libs/npm/docsify/lib/plugins/zoom-image.min.js"></script>
+        <script src="./libs/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
+        <script src="./libs/npm/docsify-pagination/dist/docsify-pagination.min.js"></script>
+        <script src="./libs/npm/prismjs@1/components/prism-bash.min.js"></script>
+        <script src="./libs/npm/docsify/lib/plugins/search.min.js"></script>
+        <script src="./libs/npm/docsify/lib/plugins/external-script.min.js"></script>
+    </body>
+</html>

BIN
libs/banner.png


+ 225 - 0
libs/codemirror/addon/comment/comment.js

@@ -0,0 +1,225 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var noOptions = {}
+    var nonWS = /[^\s\u00a0]/
+    var Pos = CodeMirror.Pos,
+        cmp = CodeMirror.cmpPos
+
+    function firstNonWS(str) {
+        var found = str.search(nonWS)
+        return found == -1 ? 0 : found
+    }
+
+    CodeMirror.commands.toggleComment = function (cm) {
+        cm.toggleComment()
+    }
+
+    CodeMirror.defineExtension('toggleComment', function (options) {
+        if (!options) options = noOptions
+        var cm = this
+        var minLine = Infinity,
+            ranges = this.listSelections(),
+            mode = null
+        for (var i = ranges.length - 1; i >= 0; i--) {
+            var from = ranges[i].from(),
+                to = ranges[i].to()
+            if (from.line >= minLine) continue
+            if (to.line >= minLine) to = Pos(minLine, 0)
+            minLine = from.line
+            if (mode == null) {
+                if (cm.uncomment(from, to, options)) mode = 'un'
+                else {
+                    cm.lineComment(from, to, options)
+                    mode = 'line'
+                }
+            } else if (mode == 'un') {
+                cm.uncomment(from, to, options)
+            } else {
+                cm.lineComment(from, to, options)
+            }
+        }
+    })
+
+    // Rough heuristic to try and detect lines that are part of multi-line string
+    function probablyInsideString(cm, pos, line) {
+        return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
+    }
+
+    function getMode(cm, pos) {
+        var mode = cm.getMode()
+        return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
+    }
+
+    CodeMirror.defineExtension('lineComment', function (from, to, options) {
+        if (!options) options = noOptions
+        var self = this,
+            mode = getMode(self, from)
+        var firstLine = self.getLine(from.line)
+        if (firstLine == null || probablyInsideString(self, from, firstLine)) return
+
+        var commentString = options.lineComment || mode.lineComment
+        if (!commentString) {
+            if (options.blockCommentStart || mode.blockCommentStart) {
+                options.fullLines = true
+                self.blockComment(from, to, options)
+            }
+            return
+        }
+
+        var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1)
+        var pad = options.padding == null ? ' ' : options.padding
+        var blankLines = options.commentBlankLines || from.line == to.line
+
+        self.operation(function () {
+            if (options.indent) {
+                var baseString = null
+                for (var i = from.line; i < end; ++i) {
+                    var line = self.getLine(i)
+                    var whitespace = line.slice(0, firstNonWS(line))
+                    if (baseString == null || baseString.length > whitespace.length) {
+                        baseString = whitespace
+                    }
+                }
+                for (var i = from.line; i < end; ++i) {
+                    var line = self.getLine(i),
+                        cut = baseString.length
+                    if (!blankLines && !nonWS.test(line)) continue
+                    if (line.slice(0, cut) != baseString) cut = firstNonWS(line)
+                    self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut))
+                }
+            } else {
+                for (var i = from.line; i < end; ++i) {
+                    if (blankLines || nonWS.test(self.getLine(i))) self.replaceRange(commentString + pad, Pos(i, 0))
+                }
+            }
+        })
+    })
+
+    CodeMirror.defineExtension('blockComment', function (from, to, options) {
+        if (!options) options = noOptions
+        var self = this,
+            mode = getMode(self, from)
+        var startString = options.blockCommentStart || mode.blockCommentStart
+        var endString = options.blockCommentEnd || mode.blockCommentEnd
+        if (!startString || !endString) {
+            if ((options.lineComment || mode.lineComment) && options.fullLines != false) self.lineComment(from, to, options)
+            return
+        }
+        if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
+
+        var end = Math.min(to.line, self.lastLine())
+        if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end
+
+        var pad = options.padding == null ? ' ' : options.padding
+        if (from.line > end) return
+
+        self.operation(function () {
+            if (options.fullLines != false) {
+                var lastLineHasText = nonWS.test(self.getLine(end))
+                self.replaceRange(pad + endString, Pos(end))
+                self.replaceRange(startString + pad, Pos(from.line, 0))
+                var lead = options.blockCommentLead || mode.blockCommentLead
+                if (lead != null) for (var i = from.line + 1; i <= end; ++i) if (i != end || lastLineHasText) self.replaceRange(lead + pad, Pos(i, 0))
+            } else {
+                var atCursor = cmp(self.getCursor('to'), to) == 0,
+                    empty = !self.somethingSelected()
+                self.replaceRange(endString, to)
+                if (atCursor) self.setSelection(empty ? to : self.getCursor('from'), to)
+                self.replaceRange(startString, from)
+            }
+        })
+    })
+
+    CodeMirror.defineExtension('uncomment', function (from, to, options) {
+        if (!options) options = noOptions
+        var self = this,
+            mode = getMode(self, from)
+        var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()),
+            start = Math.min(from.line, end)
+
+        // Try finding line comments
+        var lineString = options.lineComment || mode.lineComment,
+            lines = []
+        var pad = options.padding == null ? ' ' : options.padding,
+            didSomething
+        lineComment: {
+            if (!lineString) break lineComment
+            for (var i = start; i <= end; ++i) {
+                var line = self.getLine(i)
+                var found = line.indexOf(lineString)
+                if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1
+                if (found == -1 && nonWS.test(line)) break lineComment
+                if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment
+                lines.push(line)
+            }
+            self.operation(function () {
+                for (var i = start; i <= end; ++i) {
+                    var line = lines[i - start]
+                    var pos = line.indexOf(lineString),
+                        endPos = pos + lineString.length
+                    if (pos < 0) continue
+                    if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length
+                    didSomething = true
+                    self.replaceRange('', Pos(i, pos), Pos(i, endPos))
+                }
+            })
+            if (didSomething) return true
+        }
+
+        // Try block comments
+        var startString = options.blockCommentStart || mode.blockCommentStart
+        var endString = options.blockCommentEnd || mode.blockCommentEnd
+        if (!startString || !endString) return false
+        var lead = options.blockCommentLead || mode.blockCommentLead
+        var startLine = self.getLine(start),
+            open = startLine.indexOf(startString)
+        if (open == -1) return false
+        var endLine = end == start ? startLine : self.getLine(end)
+        var close = endLine.indexOf(endString, end == start ? open + startString.length : 0)
+        var insideStart = Pos(start, open + 1),
+            insideEnd = Pos(end, close + 1)
+        if (close == -1 || !/comment/.test(self.getTokenTypeAt(insideStart)) || !/comment/.test(self.getTokenTypeAt(insideEnd)) || self.getRange(insideStart, insideEnd, '\n').indexOf(endString) > -1)
+            return false
+
+        // Avoid killing block comments completely outside the selection.
+        // Positions of the last startString before the start of the selection, and the first endString after it.
+        var lastStart = startLine.lastIndexOf(startString, from.ch)
+        var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length)
+        if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false
+        // Positions of the first endString after the end of the selection, and the last startString before it.
+        firstEnd = endLine.indexOf(endString, to.ch)
+        var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch)
+        lastStart = firstEnd == -1 || almostLastStart == -1 ? -1 : to.ch + almostLastStart
+        if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false
+
+        self.operation(function () {
+            self.replaceRange('', Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), Pos(end, close + endString.length))
+            var openEnd = open + startString.length
+            if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length
+            self.replaceRange('', Pos(start, open), Pos(start, openEnd))
+            if (lead)
+                for (var i = start + 1; i <= end; ++i) {
+                    var line = self.getLine(i),
+                        found = line.indexOf(lead)
+                    if (found == -1 || nonWS.test(line.slice(0, found))) continue
+                    var foundEnd = found + lead.length
+                    if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length
+                    self.replaceRange('', Pos(i, found), Pos(i, foundEnd))
+                }
+        })
+        return true
+    })
+})

+ 115 - 0
libs/codemirror/addon/comment/continuecomment.js

@@ -0,0 +1,115 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    var nonspace = /\S/g
+    var repeat =
+        String.prototype.repeat ||
+        function (n) {
+            return Array(n + 1).join(this)
+        }
+    function continueComment(cm) {
+        if (cm.getOption('disableInput')) return CodeMirror.Pass
+        var ranges = cm.listSelections(),
+            mode,
+            inserts = []
+        for (var i = 0; i < ranges.length; i++) {
+            var pos = ranges[i].head
+            if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass
+            var modeHere = cm.getModeAt(pos)
+            if (!mode) mode = modeHere
+            else if (mode != modeHere) return CodeMirror.Pass
+
+            var insert = null,
+                line,
+                found
+            var blockStart = mode.blockCommentStart,
+                lineCmt = mode.lineComment
+            if (blockStart && mode.blockCommentContinue) {
+                line = cm.getLine(pos.line)
+                var end = line.lastIndexOf(mode.blockCommentEnd, pos.ch - mode.blockCommentEnd.length)
+                // 1. if this block comment ended
+                // 2. if this is actually inside a line comment
+                if (
+                    (end != -1 && end == pos.ch - mode.blockCommentEnd.length) ||
+                    (lineCmt && (found = line.lastIndexOf(lineCmt, pos.ch - 1)) > -1 && /\bcomment\b/.test(cm.getTokenTypeAt({ line: pos.line, ch: found + 1 })))
+                ) {
+                    // ...then don't continue it
+                } else if (pos.ch >= blockStart.length && (found = line.lastIndexOf(blockStart, pos.ch - blockStart.length)) > -1 && found > end) {
+                    // reuse the existing leading spaces/tabs/mixed
+                    // or build the correct indent using CM's tab/indent options
+                    if (nonspaceAfter(0, line) >= found) {
+                        insert = line.slice(0, found)
+                    } else {
+                        var tabSize = cm.options.tabSize,
+                            numTabs
+                        found = CodeMirror.countColumn(line, found, tabSize)
+                        insert = !cm.options.indentWithTabs ? repeat.call(' ', found) : repeat.call('\t', (numTabs = Math.floor(found / tabSize))) + repeat.call(' ', found - tabSize * numTabs)
+                    }
+                } else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 && found <= pos.ch && found <= nonspaceAfter(0, line)) {
+                    insert = line.slice(0, found)
+                }
+                if (insert != null) insert += mode.blockCommentContinue
+            }
+            if (insert == null && lineCmt && continueLineCommentEnabled(cm)) {
+                if (line == null) line = cm.getLine(pos.line)
+                found = line.indexOf(lineCmt)
+                // cursor at pos 0, line comment also at pos 0 => shift it down, don't continue
+                if (!pos.ch && !found) insert = ''
+                // continue only if the line starts with an optional space + line comment
+                else if (found > -1 && nonspaceAfter(0, line) >= found) {
+                    // don't continue if there's only space(s) after cursor or the end of the line
+                    insert = nonspaceAfter(pos.ch, line) > -1
+                    // but always continue if the next line starts with a line comment too
+                    if (!insert) {
+                        var next = cm.getLine(pos.line + 1) || '',
+                            nextFound = next.indexOf(lineCmt)
+                        insert = (nextFound > -1 && nonspaceAfter(0, next) >= nextFound) || null
+                    }
+                    if (insert) {
+                        insert = line.slice(0, found) + lineCmt + line.slice(found + lineCmt.length).match(/^\s*/)[0]
+                    }
+                }
+            }
+            if (insert == null) return CodeMirror.Pass
+            inserts[i] = '\n' + insert
+        }
+
+        cm.operation(function () {
+            for (var i = ranges.length - 1; i >= 0; i--) cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), '+insert')
+        })
+    }
+
+    function nonspaceAfter(ch, str) {
+        nonspace.lastIndex = ch
+        var m = nonspace.exec(str)
+        return m ? m.index : -1
+    }
+
+    function continueLineCommentEnabled(cm) {
+        var opt = cm.getOption('continueComments')
+        if (opt && typeof opt == 'object') return opt.continueLineComment !== false
+        return true
+    }
+
+    CodeMirror.defineOption('continueComments', null, function (cm, val, prev) {
+        if (prev && prev != CodeMirror.Init) cm.removeKeyMap('continueComment')
+        if (val) {
+            var key = 'Enter'
+            if (typeof val == 'string') key = val
+            else if (typeof val == 'object' && val.key) key = val.key
+            var map = { name: 'continueComment' }
+            map[key] = continueComment
+            cm.addKeyMap(map)
+        }
+    })
+})

+ 32 - 0
libs/codemirror/addon/dialog/dialog.css

@@ -0,0 +1,32 @@
+.CodeMirror-dialog {
+  position: absolute;
+  left: 0; right: 0;
+  background: inherit;
+  z-index: 15;
+  padding: .1em .8em;
+  overflow: hidden;
+  color: inherit;
+}
+
+.CodeMirror-dialog-top {
+  border-bottom: 1px solid #eee;
+  top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+  border-top: 1px solid #eee;
+  bottom: 0;
+}
+
+.CodeMirror-dialog input {
+  border: none;
+  outline: none;
+  background: transparent;
+  width: 20em;
+  color: inherit;
+  font-family: monospace;
+}
+
+.CodeMirror-dialog button {
+  font-size: 70%;
+}

+ 178 - 0
libs/codemirror/addon/dialog/dialog.js

@@ -0,0 +1,178 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Open simple dialogs on top of an editor. Relies on dialog.css.
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    function dialogDiv(cm, template, bottom) {
+        var wrap = cm.getWrapperElement()
+        var dialog
+        dialog = wrap.appendChild(document.createElement('div'))
+        if (bottom) dialog.className = 'CodeMirror-dialog CodeMirror-dialog-bottom'
+        else dialog.className = 'CodeMirror-dialog CodeMirror-dialog-top'
+
+        if (typeof template == 'string') {
+            dialog.innerHTML = template
+        } else {
+            // Assuming it's a detached DOM element.
+            dialog.appendChild(template)
+        }
+        CodeMirror.addClass(wrap, 'dialog-opened')
+        return dialog
+    }
+
+    function closeNotification(cm, newVal) {
+        if (cm.state.currentNotificationClose) cm.state.currentNotificationClose()
+        cm.state.currentNotificationClose = newVal
+    }
+
+    CodeMirror.defineExtension('openDialog', function (template, callback, options) {
+        if (!options) options = {}
+
+        closeNotification(this, null)
+
+        var dialog = dialogDiv(this, template, options.bottom)
+        var closed = false,
+            me = this
+        function close(newVal) {
+            if (typeof newVal == 'string') {
+                inp.value = newVal
+            } else {
+                if (closed) return
+                closed = true
+                CodeMirror.rmClass(dialog.parentNode, 'dialog-opened')
+                dialog.parentNode.removeChild(dialog)
+                me.focus()
+
+                if (options.onClose) options.onClose(dialog)
+            }
+        }
+
+        var inp = dialog.getElementsByTagName('input')[0],
+            button
+        if (inp) {
+            inp.focus()
+
+            if (options.value) {
+                inp.value = options.value
+                if (options.selectValueOnOpen !== false) {
+                    inp.select()
+                }
+            }
+
+            if (options.onInput)
+                CodeMirror.on(inp, 'input', function (e) {
+                    options.onInput(e, inp.value, close)
+                })
+            if (options.onKeyUp)
+                CodeMirror.on(inp, 'keyup', function (e) {
+                    options.onKeyUp(e, inp.value, close)
+                })
+
+            CodeMirror.on(inp, 'keydown', function (e) {
+                if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) {
+                    return
+                }
+                if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
+                    inp.blur()
+                    CodeMirror.e_stop(e)
+                    close()
+                }
+                if (e.keyCode == 13) callback(inp.value, e)
+            })
+
+            if (options.closeOnBlur !== false)
+                CodeMirror.on(dialog, 'focusout', function (evt) {
+                    if (evt.relatedTarget !== null) close()
+                })
+        } else if ((button = dialog.getElementsByTagName('button')[0])) {
+            CodeMirror.on(button, 'click', function () {
+                close()
+                me.focus()
+            })
+
+            if (options.closeOnBlur !== false) CodeMirror.on(button, 'blur', close)
+
+            button.focus()
+        }
+        return close
+    })
+
+    CodeMirror.defineExtension('openConfirm', function (template, callbacks, options) {
+        closeNotification(this, null)
+        var dialog = dialogDiv(this, template, options && options.bottom)
+        var buttons = dialog.getElementsByTagName('button')
+        var closed = false,
+            me = this,
+            blurring = 1
+        function close() {
+            if (closed) return
+            closed = true
+            CodeMirror.rmClass(dialog.parentNode, 'dialog-opened')
+            dialog.parentNode.removeChild(dialog)
+            me.focus()
+        }
+        buttons[0].focus()
+        for (var i = 0; i < buttons.length; ++i) {
+            var b = buttons[i]
+            ;(function (callback) {
+                CodeMirror.on(b, 'click', function (e) {
+                    CodeMirror.e_preventDefault(e)
+                    close()
+                    if (callback) callback(me)
+                })
+            })(callbacks[i])
+            CodeMirror.on(b, 'blur', function () {
+                --blurring
+                setTimeout(function () {
+                    if (blurring <= 0) close()
+                }, 200)
+            })
+            CodeMirror.on(b, 'focus', function () {
+                ++blurring
+            })
+        }
+    })
+
+    /*
+     * openNotification
+     * Opens a notification, that can be closed with an optional timer
+     * (default 5000ms timer) and always closes on click.
+     *
+     * If a notification is opened while another is opened, it will close the
+     * currently opened one and open the new one immediately.
+     */
+    CodeMirror.defineExtension('openNotification', function (template, options) {
+        closeNotification(this, close)
+        var dialog = dialogDiv(this, template, options && options.bottom)
+        var closed = false,
+            doneTimer
+        var duration = options && typeof options.duration !== 'undefined' ? options.duration : 5000
+
+        function close() {
+            if (closed) return
+            closed = true
+            clearTimeout(doneTimer)
+            CodeMirror.rmClass(dialog.parentNode, 'dialog-opened')
+            dialog.parentNode.removeChild(dialog)
+        }
+
+        CodeMirror.on(dialog, 'click', function (e) {
+            CodeMirror.e_preventDefault(e)
+            close()
+        })
+
+        if (duration) doneTimer = setTimeout(close, duration)
+
+        return close
+    })
+})

+ 47 - 0
libs/codemirror/addon/display/autorefresh.js

@@ -0,0 +1,47 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineOption('autoRefresh', false, function (cm, val) {
+        if (cm.state.autoRefresh) {
+            stopListening(cm, cm.state.autoRefresh)
+            cm.state.autoRefresh = null
+        }
+        if (val && cm.display.wrapper.offsetHeight == 0) startListening(cm, (cm.state.autoRefresh = { delay: val.delay || 250 }))
+    })
+
+    function startListening(cm, state) {
+        function check() {
+            if (cm.display.wrapper.offsetHeight) {
+                stopListening(cm, state)
+                if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight) cm.refresh()
+            } else {
+                state.timeout = setTimeout(check, state.delay)
+            }
+        }
+        state.timeout = setTimeout(check, state.delay)
+        state.hurry = function () {
+            clearTimeout(state.timeout)
+            state.timeout = setTimeout(check, 50)
+        }
+        CodeMirror.on(window, 'mouseup', state.hurry)
+        CodeMirror.on(window, 'keyup', state.hurry)
+    }
+
+    function stopListening(_cm, state) {
+        clearTimeout(state.timeout)
+        CodeMirror.off(window, 'mouseup', state.hurry)
+        CodeMirror.off(window, 'keyup', state.hurry)
+    }
+})

+ 6 - 0
libs/codemirror/addon/display/fullscreen.css

@@ -0,0 +1,6 @@
+.CodeMirror-fullscreen {
+  position: fixed;
+  top: 0; left: 0; right: 0; bottom: 0;
+  height: auto;
+  z-index: 9;
+}

+ 43 - 0
libs/codemirror/addon/display/fullscreen.js

@@ -0,0 +1,43 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineOption('fullScreen', false, function (cm, val, old) {
+        if (old == CodeMirror.Init) old = false
+        if (!old == !val) return
+        if (val) setFullscreen(cm)
+        else setNormal(cm)
+    })
+
+    function setFullscreen(cm) {
+        var wrap = cm.getWrapperElement()
+        cm.state.fullScreenRestore = { scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, width: wrap.style.width, height: wrap.style.height }
+        wrap.style.width = ''
+        wrap.style.height = 'auto'
+        wrap.className += ' CodeMirror-fullscreen'
+        document.documentElement.style.overflow = 'hidden'
+        cm.refresh()
+    }
+
+    function setNormal(cm) {
+        var wrap = cm.getWrapperElement()
+        wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, '')
+        document.documentElement.style.overflow = ''
+        var info = cm.state.fullScreenRestore
+        wrap.style.width = info.width
+        wrap.style.height = info.height
+        window.scrollTo(info.scrollLeft, info.scrollTop)
+        cm.refresh()
+    }
+})

+ 141 - 0
libs/codemirror/addon/display/panel.js

@@ -0,0 +1,141 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    CodeMirror.defineExtension('addPanel', function (node, options) {
+        options = options || {}
+
+        if (!this.state.panels) initPanels(this)
+
+        var info = this.state.panels
+        var wrapper = info.wrapper
+        var cmWrapper = this.getWrapperElement()
+        var replace = options.replace instanceof Panel && !options.replace.cleared
+
+        if (options.after instanceof Panel && !options.after.cleared) {
+            wrapper.insertBefore(node, options.before.node.nextSibling)
+        } else if (options.before instanceof Panel && !options.before.cleared) {
+            wrapper.insertBefore(node, options.before.node)
+        } else if (replace) {
+            wrapper.insertBefore(node, options.replace.node)
+            options.replace.clear(true)
+        } else if (options.position == 'bottom') {
+            wrapper.appendChild(node)
+        } else if (options.position == 'before-bottom') {
+            wrapper.insertBefore(node, cmWrapper.nextSibling)
+        } else if (options.position == 'after-top') {
+            wrapper.insertBefore(node, cmWrapper)
+        } else {
+            wrapper.insertBefore(node, wrapper.firstChild)
+        }
+
+        var height = (options && options.height) || node.offsetHeight
+
+        var panel = new Panel(this, node, options, height)
+        info.panels.push(panel)
+
+        this.setSize()
+        if (options.stable && isAtTop(this, node)) this.scrollTo(null, this.getScrollInfo().top + height)
+
+        return panel
+    })
+
+    function Panel(cm, node, options, height) {
+        this.cm = cm
+        this.node = node
+        this.options = options
+        this.height = height
+        this.cleared = false
+    }
+
+    /* when skipRemove is true, clear() was called from addPanel().
+     * Thus removePanels() should not be called (issue 5518) */
+    Panel.prototype.clear = function (skipRemove) {
+        if (this.cleared) return
+        this.cleared = true
+        var info = this.cm.state.panels
+        info.panels.splice(info.panels.indexOf(this), 1)
+        this.cm.setSize()
+        if (this.options.stable && isAtTop(this.cm, this.node)) this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height)
+        info.wrapper.removeChild(this.node)
+        if (info.panels.length == 0 && !skipRemove) removePanels(this.cm)
+    }
+
+    Panel.prototype.changed = function () {
+        this.height = this.node.getBoundingClientRect().height
+        this.cm.setSize()
+    }
+
+    function initPanels(cm) {
+        var wrap = cm.getWrapperElement()
+        var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle
+        var height = parseInt(style.height)
+        var info = (cm.state.panels = {
+            setHeight: wrap.style.height,
+            panels: [],
+            wrapper: document.createElement('div'),
+        })
+        var hasFocus = cm.hasFocus(),
+            scrollPos = cm.getScrollInfo()
+        wrap.parentNode.insertBefore(info.wrapper, wrap)
+        info.wrapper.appendChild(wrap)
+        cm.scrollTo(scrollPos.left, scrollPos.top)
+        if (hasFocus) cm.focus()
+
+        cm._setSize = cm.setSize
+        if (height != null)
+            cm.setSize = function (width, newHeight) {
+                if (!newHeight) newHeight = info.wrapper.offsetHeight
+                info.setHeight = newHeight
+                if (typeof newHeight != 'number') {
+                    var px = /^(\d+\.?\d*)px$/.exec(newHeight)
+                    if (px) {
+                        newHeight = Number(px[1])
+                    } else {
+                        info.wrapper.style.height = newHeight
+                        newHeight = info.wrapper.offsetHeight
+                    }
+                }
+                var editorheight =
+                    newHeight -
+                    info.panels
+                        .map(function (p) {
+                            return p.node.getBoundingClientRect().height
+                        })
+                        .reduce(function (a, b) {
+                            return a + b
+                        }, 0)
+                cm._setSize(width, editorheight)
+                height = newHeight
+            }
+    }
+
+    function removePanels(cm) {
+        var info = cm.state.panels
+        cm.state.panels = null
+
+        var wrap = cm.getWrapperElement()
+        var hasFocus = cm.hasFocus(),
+            scrollPos = cm.getScrollInfo()
+        info.wrapper.parentNode.replaceChild(wrap, info.wrapper)
+        cm.scrollTo(scrollPos.left, scrollPos.top)
+        if (hasFocus) cm.focus()
+        wrap.style.height = info.setHeight
+        cm.setSize = cm._setSize
+        cm.setSize()
+    }
+
+    function isAtTop(cm, dom) {
+        for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling) if (sibling == cm.getWrapperElement()) return true
+        return false
+    }
+})

+ 86 - 0
libs/codemirror/addon/display/placeholder.js

@@ -0,0 +1,86 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    CodeMirror.defineOption('placeholder', '', function (cm, val, old) {
+        var prev = old && old != CodeMirror.Init
+        if (val && !prev) {
+            cm.on('blur', onBlur)
+            cm.on('change', onChange)
+            cm.on('swapDoc', onChange)
+            CodeMirror.on(
+                cm.getInputField(),
+                'compositionupdate',
+                (cm.state.placeholderCompose = function () {
+                    onComposition(cm)
+                })
+            )
+            onChange(cm)
+        } else if (!val && prev) {
+            cm.off('blur', onBlur)
+            cm.off('change', onChange)
+            cm.off('swapDoc', onChange)
+            CodeMirror.off(cm.getInputField(), 'compositionupdate', cm.state.placeholderCompose)
+            clearPlaceholder(cm)
+            var wrapper = cm.getWrapperElement()
+            wrapper.className = wrapper.className.replace(' CodeMirror-empty', '')
+        }
+
+        if (val && !cm.hasFocus()) onBlur(cm)
+    })
+
+    function clearPlaceholder(cm) {
+        if (cm.state.placeholder) {
+            cm.state.placeholder.parentNode.removeChild(cm.state.placeholder)
+            cm.state.placeholder = null
+        }
+    }
+    function setPlaceholder(cm) {
+        clearPlaceholder(cm)
+        var elt = (cm.state.placeholder = document.createElement('pre'))
+        elt.style.cssText = 'height: 0; overflow: visible'
+        elt.style.direction = cm.getOption('direction')
+        elt.className = 'CodeMirror-placeholder CodeMirror-line-like'
+        var placeHolder = cm.getOption('placeholder')
+        if (typeof placeHolder == 'string') placeHolder = document.createTextNode(placeHolder)
+        elt.appendChild(placeHolder)
+        cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild)
+    }
+
+    function onComposition(cm) {
+        setTimeout(function () {
+            var empty = false
+            if (cm.lineCount() == 1) {
+                var input = cm.getInputField()
+                empty = input.nodeName == 'TEXTAREA' ? !cm.getLine(0).length : !/[^\u200b]/.test(input.querySelector('.CodeMirror-line').textContent)
+            }
+            if (empty) setPlaceholder(cm)
+            else clearPlaceholder(cm)
+        }, 20)
+    }
+
+    function onBlur(cm) {
+        if (isEmpty(cm)) setPlaceholder(cm)
+    }
+    function onChange(cm) {
+        var wrapper = cm.getWrapperElement(),
+            empty = isEmpty(cm)
+        wrapper.className = wrapper.className.replace(' CodeMirror-empty', '') + (empty ? ' CodeMirror-empty' : '')
+
+        if (empty) setPlaceholder(cm)
+        else clearPlaceholder(cm)
+    }
+
+    function isEmpty(cm) {
+        return cm.lineCount() === 1 && cm.getLine(0) === ''
+    }
+})

+ 54 - 0
libs/codemirror/addon/display/rulers.js

@@ -0,0 +1,54 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineOption('rulers', false, function (cm, val) {
+        if (cm.state.rulerDiv) {
+            cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv)
+            cm.state.rulerDiv = null
+            cm.off('refresh', drawRulers)
+        }
+        if (val && val.length) {
+            cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement('div'), cm.display.lineSpace)
+            cm.state.rulerDiv.className = 'CodeMirror-rulers'
+            drawRulers(cm)
+            cm.on('refresh', drawRulers)
+        }
+    })
+
+    function drawRulers(cm) {
+        cm.state.rulerDiv.textContent = ''
+        var val = cm.getOption('rulers')
+        var cw = cm.defaultCharWidth()
+        var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), 'div').left
+        cm.state.rulerDiv.style.minHeight = cm.display.scroller.offsetHeight + 30 + 'px'
+        for (var i = 0; i < val.length; i++) {
+            var elt = document.createElement('div')
+            elt.className = 'CodeMirror-ruler'
+            var col,
+                conf = val[i]
+            if (typeof conf == 'number') {
+                col = conf
+            } else {
+                col = conf.column
+                if (conf.className) elt.className += ' ' + conf.className
+                if (conf.color) elt.style.borderColor = conf.color
+                if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle
+                if (conf.width) elt.style.borderLeftWidth = conf.width
+            }
+            elt.style.left = left + col * cw + 'px'
+            cm.state.rulerDiv.appendChild(elt)
+        }
+    }
+})

+ 201 - 0
libs/codemirror/addon/edit/closebrackets.js

@@ -0,0 +1,201 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    var defaults = {
+        pairs: '()[]{}\'\'""',
+        closeBefore: ')]}\'":;>',
+        triples: '',
+        explode: '[]{}',
+    }
+
+    var Pos = CodeMirror.Pos
+
+    CodeMirror.defineOption('autoCloseBrackets', false, function (cm, val, old) {
+        if (old && old != CodeMirror.Init) {
+            cm.removeKeyMap(keyMap)
+            cm.state.closeBrackets = null
+        }
+        if (val) {
+            ensureBound(getOption(val, 'pairs'))
+            cm.state.closeBrackets = val
+            cm.addKeyMap(keyMap)
+        }
+    })
+
+    function getOption(conf, name) {
+        if (name == 'pairs' && typeof conf == 'string') return conf
+        if (typeof conf == 'object' && conf[name] != null) return conf[name]
+        return defaults[name]
+    }
+
+    var keyMap = { Backspace: handleBackspace, Enter: handleEnter }
+    function ensureBound(chars) {
+        for (var i = 0; i < chars.length; i++) {
+            var ch = chars.charAt(i),
+                key = "'" + ch + "'"
+            if (!keyMap[key]) keyMap[key] = handler(ch)
+        }
+    }
+    ensureBound(defaults.pairs + '`')
+
+    function handler(ch) {
+        return function (cm) {
+            return handleChar(cm, ch)
+        }
+    }
+
+    function getConfig(cm) {
+        var deflt = cm.state.closeBrackets
+        if (!deflt || deflt.override) return deflt
+        var mode = cm.getModeAt(cm.getCursor())
+        return mode.closeBrackets || deflt
+    }
+
+    function handleBackspace(cm) {
+        var conf = getConfig(cm)
+        if (!conf || cm.getOption('disableInput')) return CodeMirror.Pass
+
+        var pairs = getOption(conf, 'pairs')
+        var ranges = cm.listSelections()
+        for (var i = 0; i < ranges.length; i++) {
+            if (!ranges[i].empty()) return CodeMirror.Pass
+            var around = charsAround(cm, ranges[i].head)
+            if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass
+        }
+        for (var i = ranges.length - 1; i >= 0; i--) {
+            var cur = ranges[i].head
+            cm.replaceRange('', Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), '+delete')
+        }
+    }
+
+    function handleEnter(cm) {
+        var conf = getConfig(cm)
+        var explode = conf && getOption(conf, 'explode')
+        if (!explode || cm.getOption('disableInput')) return CodeMirror.Pass
+
+        var ranges = cm.listSelections()
+        for (var i = 0; i < ranges.length; i++) {
+            if (!ranges[i].empty()) return CodeMirror.Pass
+            var around = charsAround(cm, ranges[i].head)
+            if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass
+        }
+        cm.operation(function () {
+            var linesep = cm.lineSeparator() || '\n'
+            cm.replaceSelection(linesep + linesep, null)
+            moveSel(cm, -1)
+            ranges = cm.listSelections()
+            for (var i = 0; i < ranges.length; i++) {
+                var line = ranges[i].head.line
+                cm.indentLine(line, null, true)
+                cm.indentLine(line + 1, null, true)
+            }
+        })
+    }
+
+    function moveSel(cm, dir) {
+        var newRanges = [],
+            ranges = cm.listSelections(),
+            primary = 0
+        for (var i = 0; i < ranges.length; i++) {
+            var range = ranges[i]
+            if (range.head == cm.getCursor()) primary = i
+            var pos = range.head.ch || dir > 0 ? { line: range.head.line, ch: range.head.ch + dir } : { line: range.head.line - 1 }
+            newRanges.push({ anchor: pos, head: pos })
+        }
+        cm.setSelections(newRanges, primary)
+    }
+
+    function contractSelection(sel) {
+        var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0
+        return { anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)), head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1)) }
+    }
+
+    function handleChar(cm, ch) {
+        var conf = getConfig(cm)
+        if (!conf || cm.getOption('disableInput')) return CodeMirror.Pass
+
+        var pairs = getOption(conf, 'pairs')
+        var pos = pairs.indexOf(ch)
+        if (pos == -1) return CodeMirror.Pass
+
+        var closeBefore = getOption(conf, 'closeBefore')
+
+        var triples = getOption(conf, 'triples')
+
+        var identical = pairs.charAt(pos + 1) == ch
+        var ranges = cm.listSelections()
+        var opening = pos % 2 == 0
+
+        var type
+        for (var i = 0; i < ranges.length; i++) {
+            var range = ranges[i],
+                cur = range.head,
+                curType
+            var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1))
+            if (opening && !range.empty()) {
+                curType = 'surround'
+            } else if ((identical || !opening) && next == ch) {
+                if (identical && stringStartsAfter(cm, cur)) curType = 'both'
+                else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch) curType = 'skipThree'
+                else curType = 'skip'
+            } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 && cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
+                if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass
+                curType = 'addFour'
+            } else if (identical) {
+                var prev = cur.ch == 0 ? ' ' : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
+                if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = 'both'
+                else return CodeMirror.Pass
+            } else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
+                curType = 'both'
+            } else {
+                return CodeMirror.Pass
+            }
+            if (!type) type = curType
+            else if (type != curType) return CodeMirror.Pass
+        }
+
+        var left = pos % 2 ? pairs.charAt(pos - 1) : ch
+        var right = pos % 2 ? ch : pairs.charAt(pos + 1)
+        cm.operation(function () {
+            if (type == 'skip') {
+                moveSel(cm, 1)
+            } else if (type == 'skipThree') {
+                moveSel(cm, 3)
+            } else if (type == 'surround') {
+                var sels = cm.getSelections()
+                for (var i = 0; i < sels.length; i++) sels[i] = left + sels[i] + right
+                cm.replaceSelections(sels, 'around')
+                sels = cm.listSelections().slice()
+                for (var i = 0; i < sels.length; i++) sels[i] = contractSelection(sels[i])
+                cm.setSelections(sels)
+            } else if (type == 'both') {
+                cm.replaceSelection(left + right, null)
+                cm.triggerElectric(left + right)
+                moveSel(cm, -1)
+            } else if (type == 'addFour') {
+                cm.replaceSelection(left + left + left + left, 'before')
+                moveSel(cm, 1)
+            }
+        })
+    }
+
+    function charsAround(cm, pos) {
+        var str = cm.getRange(Pos(pos.line, pos.ch - 1), Pos(pos.line, pos.ch + 1))
+        return str.length == 2 ? str : null
+    }
+
+    function stringStartsAfter(cm, pos) {
+        var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
+        return /\bstring/.test(token.type) && token.start == pos.ch && (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
+    }
+})

+ 218 - 0
libs/codemirror/addon/edit/closetag.js

@@ -0,0 +1,218 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/**
+ * Tag-closer extension for CodeMirror.
+ *
+ * This extension adds an "autoCloseTags" option that can be set to
+ * either true to get the default behavior, or an object to further
+ * configure its behavior.
+ *
+ * These are supported options:
+ *
+ * `whenClosing` (default true)
+ *   Whether to autoclose when the '/' of a closing tag is typed.
+ * `whenOpening` (default true)
+ *   Whether to autoclose the tag when the final '>' of an opening
+ *   tag is typed.
+ * `dontCloseTags` (default is empty tags for HTML, none for XML)
+ *   An array of tag names that should not be autoclosed.
+ * `indentTags` (default is block tags for HTML, none for XML)
+ *   An array of tag names that should, when opened, cause a
+ *   blank line to be added inside the tag, and the blank line and
+ *   closing line to be indented.
+ * `emptyTags` (default is none)
+ *   An array of XML tag names that should be autoclosed with '/>'.
+ *
+ * See demos/closetag.html for a usage example.
+ */
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('../fold/xml-fold'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', '../fold/xml-fold'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    CodeMirror.defineOption('autoCloseTags', false, function (cm, val, old) {
+        if (old != CodeMirror.Init && old) cm.removeKeyMap('autoCloseTags')
+        if (!val) return
+        var map = { name: 'autoCloseTags' }
+        if (typeof val != 'object' || val.whenClosing !== false)
+            map["'/'"] = function (cm) {
+                return autoCloseSlash(cm)
+            }
+        if (typeof val != 'object' || val.whenOpening !== false)
+            map["'>'"] = function (cm) {
+                return autoCloseGT(cm)
+            }
+        cm.addKeyMap(map)
+    })
+
+    var htmlDontClose = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr']
+    var htmlIndent = [
+        'applet',
+        'blockquote',
+        'body',
+        'button',
+        'div',
+        'dl',
+        'fieldset',
+        'form',
+        'frameset',
+        'h1',
+        'h2',
+        'h3',
+        'h4',
+        'h5',
+        'h6',
+        'head',
+        'html',
+        'iframe',
+        'layer',
+        'legend',
+        'object',
+        'ol',
+        'p',
+        'select',
+        'table',
+        'ul',
+    ]
+
+    function autoCloseGT(cm) {
+        if (cm.getOption('disableInput')) return CodeMirror.Pass
+        var ranges = cm.listSelections(),
+            replacements = []
+        var opt = cm.getOption('autoCloseTags')
+        for (var i = 0; i < ranges.length; i++) {
+            if (!ranges[i].empty()) return CodeMirror.Pass
+            var pos = ranges[i].head,
+                tok = cm.getTokenAt(pos)
+            var inner = CodeMirror.innerMode(cm.getMode(), tok.state),
+                state = inner.state
+            var tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state)
+            var tagName = tagInfo && tagInfo.name
+            if (!tagName) return CodeMirror.Pass
+
+            var html = inner.mode.configuration == 'html'
+            var dontCloseTags = (typeof opt == 'object' && opt.dontCloseTags) || (html && htmlDontClose)
+            var indentTags = (typeof opt == 'object' && opt.indentTags) || (html && htmlIndent)
+
+            if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch)
+            var lowerTagName = tagName.toLowerCase()
+            // Don't process the '>' at the end of an end-tag or self-closing tag
+            if (
+                !tagName ||
+                (tok.type == 'string' && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1)) ||
+                (tok.type == 'tag' && tagInfo.close) ||
+                tok.string.indexOf('/') == pos.ch - tok.start - 1 || // match something like <someTagName />
+                (dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) ||
+                closingTagExists(cm, (inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)) || [], tagName, pos, true)
+            )
+                return CodeMirror.Pass
+
+            var emptyTags = typeof opt == 'object' && opt.emptyTags
+            if (emptyTags && indexOf(emptyTags, tagName) > -1) {
+                replacements[i] = { text: '/>', newPos: CodeMirror.Pos(pos.line, pos.ch + 2) }
+                continue
+            }
+
+            var indent = indentTags && indexOf(indentTags, lowerTagName) > -1
+            replacements[i] = { indent: indent, text: '>' + (indent ? '\n\n' : '') + '</' + tagName + '>', newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1) }
+        }
+
+        var dontIndentOnAutoClose = typeof opt == 'object' && opt.dontIndentOnAutoClose
+        for (var i = ranges.length - 1; i >= 0; i--) {
+            var info = replacements[i]
+            cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, '+insert')
+            var sel = cm.listSelections().slice(0)
+            sel[i] = { head: info.newPos, anchor: info.newPos }
+            cm.setSelections(sel)
+            if (!dontIndentOnAutoClose && info.indent) {
+                cm.indentLine(info.newPos.line, null, true)
+                cm.indentLine(info.newPos.line + 1, null, true)
+            }
+        }
+    }
+
+    function autoCloseCurrent(cm, typingSlash) {
+        var ranges = cm.listSelections(),
+            replacements = []
+        var head = typingSlash ? '/' : '</'
+        var opt = cm.getOption('autoCloseTags')
+        var dontIndentOnAutoClose = typeof opt == 'object' && opt.dontIndentOnSlash
+        for (var i = 0; i < ranges.length; i++) {
+            if (!ranges[i].empty()) return CodeMirror.Pass
+            var pos = ranges[i].head,
+                tok = cm.getTokenAt(pos)
+            var inner = CodeMirror.innerMode(cm.getMode(), tok.state),
+                state = inner.state
+            if (typingSlash && (tok.type == 'string' || tok.string.charAt(0) != '<' || tok.start != pos.ch - 1)) return CodeMirror.Pass
+            // Kludge to get around the fact that we are not in XML mode
+            // when completing in JS/CSS snippet in htmlmixed mode. Does not
+            // work for other XML embedded languages (there is no general
+            // way to go from a mixed mode to its current XML state).
+            var replacement,
+                mixed = inner.mode.name != 'xml' && cm.getMode().name == 'htmlmixed'
+            if (mixed && inner.mode.name == 'javascript') {
+                replacement = head + 'script'
+            } else if (mixed && inner.mode.name == 'css') {
+                replacement = head + 'style'
+            } else {
+                var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)
+                var top = context.length ? context[context.length - 1] : ''
+                if (!context || (context.length && closingTagExists(cm, context, top, pos))) return CodeMirror.Pass
+                replacement = head + top
+            }
+            if (cm.getLine(pos.line).charAt(tok.end) != '>') replacement += '>'
+            replacements[i] = replacement
+        }
+        cm.replaceSelections(replacements)
+        ranges = cm.listSelections()
+        if (!dontIndentOnAutoClose) {
+            for (var i = 0; i < ranges.length; i++) if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line) cm.indentLine(ranges[i].head.line)
+        }
+    }
+
+    function autoCloseSlash(cm) {
+        if (cm.getOption('disableInput')) return CodeMirror.Pass
+        return autoCloseCurrent(cm, true)
+    }
+
+    CodeMirror.commands.closeTag = function (cm) {
+        return autoCloseCurrent(cm)
+    }
+
+    function indexOf(collection, elt) {
+        if (collection.indexOf) return collection.indexOf(elt)
+        for (var i = 0, e = collection.length; i < e; ++i) if (collection[i] == elt) return i
+        return -1
+    }
+
+    // If xml-fold is loaded, we use its functionality to try and verify
+    // whether a given tag is actually unclosed.
+    function closingTagExists(cm, context, tagName, pos, newTag) {
+        if (!CodeMirror.scanForClosingTag) return false
+        var end = Math.min(cm.lastLine() + 1, pos.line + 500)
+        var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end)
+        if (!nextClose || nextClose.tag != tagName) return false
+        // If the immediate wrapping context contains onCx instances of
+        // the same tag, a closing tag only exists if there are at least
+        // that many closing tags of that type following.
+        var onCx = newTag ? 1 : 0
+        for (var i = context.length - 1; i >= 0; i--) {
+            if (context[i] == tagName) ++onCx
+            else break
+        }
+        pos = nextClose.to
+        for (var i = 1; i < onCx; i++) {
+            var next = CodeMirror.scanForClosingTag(cm, pos, null, end)
+            if (!next || next.tag != tagName) return false
+            pos = next.to
+        }
+        return true
+    }
+})

+ 122 - 0
libs/codemirror/addon/edit/continuelist.js

@@ -0,0 +1,122 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
+        emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
+        unorderedListRE = /[*+-]\s/
+
+    CodeMirror.commands.newlineAndIndentContinueMarkdownList = function (cm) {
+        if (cm.getOption('disableInput')) return CodeMirror.Pass
+        var ranges = cm.listSelections(),
+            replacements = []
+        for (var i = 0; i < ranges.length; i++) {
+            var pos = ranges[i].head
+
+            // If we're not in Markdown mode, fall back to normal newlineAndIndent
+            var eolState = cm.getStateAfter(pos.line)
+            var inner = CodeMirror.innerMode(cm.getMode(), eolState)
+            if (inner.mode.name !== 'markdown' && inner.mode.helperType !== 'markdown') {
+                cm.execCommand('newlineAndIndent')
+                return
+            } else {
+                eolState = inner.state
+            }
+
+            var inList = eolState.list !== false
+            var inQuote = eolState.quote !== 0
+
+            var line = cm.getLine(pos.line),
+                match = listRE.exec(line)
+            var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch))
+            if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
+                cm.execCommand('newlineAndIndent')
+                return
+            }
+            if (emptyListRE.test(line)) {
+                var endOfQuote = inQuote && />\s*$/.test(line)
+                var endOfList = !/>\s*$/.test(line)
+                if (endOfQuote || endOfList)
+                    cm.replaceRange(
+                        '',
+                        {
+                            line: pos.line,
+                            ch: 0,
+                        },
+                        {
+                            line: pos.line,
+                            ch: pos.ch + 1,
+                        }
+                    )
+                replacements[i] = '\n'
+            } else {
+                var indent = match[1],
+                    after = match[5]
+                var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf('>') >= 0)
+                var bullet = numbered ? parseInt(match[3], 10) + 1 + match[4] : match[2].replace('x', ' ')
+                replacements[i] = '\n' + indent + bullet + after
+
+                if (numbered) incrementRemainingMarkdownListNumbers(cm, pos)
+            }
+        }
+
+        cm.replaceSelections(replacements)
+    }
+
+    // Auto-updating Markdown list numbers when a new item is added to the
+    // middle of a list
+    function incrementRemainingMarkdownListNumbers(cm, pos) {
+        var startLine = pos.line,
+            lookAhead = 0,
+            skipCount = 0
+        var startItem = listRE.exec(cm.getLine(startLine)),
+            startIndent = startItem[1]
+
+        do {
+            lookAhead += 1
+            var nextLineNumber = startLine + lookAhead
+            var nextLine = cm.getLine(nextLineNumber),
+                nextItem = listRE.exec(nextLine)
+
+            if (nextItem) {
+                var nextIndent = nextItem[1]
+                var newNumber = parseInt(startItem[3], 10) + lookAhead - skipCount
+                var nextNumber = parseInt(nextItem[3], 10),
+                    itemNumber = nextNumber
+
+                if (startIndent === nextIndent && !isNaN(nextNumber)) {
+                    if (newNumber === nextNumber) itemNumber = nextNumber + 1
+                    if (newNumber > nextNumber) itemNumber = newNumber + 1
+                    cm.replaceRange(
+                        nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
+                        {
+                            line: nextLineNumber,
+                            ch: 0,
+                        },
+                        {
+                            line: nextLineNumber,
+                            ch: nextLine.length,
+                        }
+                    )
+                } else {
+                    if (startIndent.length > nextIndent.length) return
+                    // This doesn't run if the next line immediately indents, as it is
+                    // not clear of the users intention (new indented item or same level)
+                    if (startIndent.length < nextIndent.length && lookAhead === 1) return
+                    skipCount += 1
+                }
+            }
+        } while (nextItem)
+    }
+})

+ 160 - 0
libs/codemirror/addon/edit/matchbrackets.js

@@ -0,0 +1,160 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && (document.documentMode == null || document.documentMode < 8)
+
+    var Pos = CodeMirror.Pos
+
+    var matching = { '(': ')>', ')': '(<', '[': ']>', ']': '[<', '{': '}>', '}': '{<', '<': '>>', '>': '<<' }
+
+    function bracketRegex(config) {
+        return (config && config.bracketRegex) || /[(){}[\]]/
+    }
+
+    function findMatchingBracket(cm, where, config) {
+        var line = cm.getLineHandle(where.line),
+            pos = where.ch - 1
+        var afterCursor = config && config.afterCursor
+        if (afterCursor == null) afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
+        var re = bracketRegex(config)
+
+        // A cursor is defined as between two characters, but in in vim command mode
+        // (i.e. not insert mode), the cursor is visually represented as a
+        // highlighted box on top of the 2nd character. Otherwise, we allow matches
+        // from before or after the cursor.
+        var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) || (re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)])
+        if (!match) return null
+        var dir = match.charAt(1) == '>' ? 1 : -1
+        if (config && config.strict && dir > 0 != (pos == where.ch)) return null
+        var style = cm.getTokenTypeAt(Pos(where.line, pos + 1))
+
+        var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config)
+        if (found == null) return null
+        return { from: Pos(where.line, pos), to: found && found.pos, match: found && found.ch == match.charAt(0), forward: dir > 0 }
+    }
+
+    // bracketRegex is used to specify which type of bracket to scan
+    // should be a regexp, e.g. /[[\]]/
+    //
+    // Note: If "where" is on an open bracket, then this bracket is ignored.
+    //
+    // Returns false when no bracket was found, null when it reached
+    // maxScanLines and gave up
+    function scanForBracket(cm, where, dir, style, config) {
+        var maxScanLen = (config && config.maxScanLineLength) || 10000
+        var maxScanLines = (config && config.maxScanLines) || 1000
+
+        var stack = []
+        var re = bracketRegex(config)
+        var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) : Math.max(cm.firstLine() - 1, where.line - maxScanLines)
+        for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
+            var line = cm.getLine(lineNo)
+            if (!line) continue
+            var pos = dir > 0 ? 0 : line.length - 1,
+                end = dir > 0 ? line.length : -1
+            if (line.length > maxScanLen) continue
+            if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0)
+            for (; pos != end; pos += dir) {
+                var ch = line.charAt(pos)
+                if (re.test(ch) && (style === undefined || (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || '') == (style || ''))) {
+                    var match = matching[ch]
+                    if (match && (match.charAt(1) == '>') == dir > 0) stack.push(ch)
+                    else if (!stack.length) return { pos: Pos(lineNo, pos), ch: ch }
+                    else stack.pop()
+                }
+            }
+        }
+        return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null
+    }
+
+    function matchBrackets(cm, autoclear, config) {
+        // Disable brace matching in long lines, since it'll cause hugely slow updates
+        var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000,
+            highlightNonMatching = config && config.highlightNonMatching
+        var marks = [],
+            ranges = cm.listSelections()
+        for (var i = 0; i < ranges.length; i++) {
+            var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config)
+            if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) {
+                var style = match.match ? 'CodeMirror-matchingbracket' : 'CodeMirror-nonmatchingbracket'
+                marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), { className: style }))
+                if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), { className: style }))
+            }
+        }
+
+        if (marks.length) {
+            // Kludge to work around the IE bug from issue #1193, where text
+            // input stops going to the textarea whenever this fires.
+            if (ie_lt8 && cm.state.focused) cm.focus()
+
+            var clear = function () {
+                cm.operation(function () {
+                    for (var i = 0; i < marks.length; i++) marks[i].clear()
+                })
+            }
+            if (autoclear) setTimeout(clear, 800)
+            else return clear
+        }
+    }
+
+    function doMatchBrackets(cm) {
+        cm.operation(function () {
+            if (cm.state.matchBrackets.currentlyHighlighted) {
+                cm.state.matchBrackets.currentlyHighlighted()
+                cm.state.matchBrackets.currentlyHighlighted = null
+            }
+            cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets)
+        })
+    }
+
+    function clearHighlighted(cm) {
+        if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
+            cm.state.matchBrackets.currentlyHighlighted()
+            cm.state.matchBrackets.currentlyHighlighted = null
+        }
+    }
+
+    CodeMirror.defineOption('matchBrackets', false, function (cm, val, old) {
+        if (old && old != CodeMirror.Init) {
+            cm.off('cursorActivity', doMatchBrackets)
+            cm.off('focus', doMatchBrackets)
+            cm.off('blur', clearHighlighted)
+            clearHighlighted(cm)
+        }
+        if (val) {
+            cm.state.matchBrackets = typeof val == 'object' ? val : {}
+            cm.on('cursorActivity', doMatchBrackets)
+            cm.on('focus', doMatchBrackets)
+            cm.on('blur', clearHighlighted)
+        }
+    })
+
+    CodeMirror.defineExtension('matchBrackets', function () {
+        matchBrackets(this, true)
+    })
+    CodeMirror.defineExtension('findMatchingBracket', function (pos, config, oldConfig) {
+        // Backwards-compatibility kludge
+        if (oldConfig || typeof config == 'boolean') {
+            if (!oldConfig) {
+                config = config ? { strict: true } : null
+            } else {
+                oldConfig.strict = config
+                config = oldConfig
+            }
+        }
+        return findMatchingBracket(this, pos, config)
+    })
+    CodeMirror.defineExtension('scanForBracket', function (pos, dir, style, config) {
+        return scanForBracket(this, pos, dir, style, config)
+    })
+})

+ 68 - 0
libs/codemirror/addon/edit/matchtags.js

@@ -0,0 +1,68 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('../fold/xml-fold'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', '../fold/xml-fold'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineOption('matchTags', false, function (cm, val, old) {
+        if (old && old != CodeMirror.Init) {
+            cm.off('cursorActivity', doMatchTags)
+            cm.off('viewportChange', maybeUpdateMatch)
+            clear(cm)
+        }
+        if (val) {
+            cm.state.matchBothTags = typeof val == 'object' && val.bothTags
+            cm.on('cursorActivity', doMatchTags)
+            cm.on('viewportChange', maybeUpdateMatch)
+            doMatchTags(cm)
+        }
+    })
+
+    function clear(cm) {
+        if (cm.state.tagHit) cm.state.tagHit.clear()
+        if (cm.state.tagOther) cm.state.tagOther.clear()
+        cm.state.tagHit = cm.state.tagOther = null
+    }
+
+    function doMatchTags(cm) {
+        cm.state.failedTagMatch = false
+        cm.operation(function () {
+            clear(cm)
+            if (cm.somethingSelected()) return
+            var cur = cm.getCursor(),
+                range = cm.getViewport()
+            range.from = Math.min(range.from, cur.line)
+            range.to = Math.max(cur.line + 1, range.to)
+            var match = CodeMirror.findMatchingTag(cm, cur, range)
+            if (!match) return
+            if (cm.state.matchBothTags) {
+                var hit = match.at == 'open' ? match.open : match.close
+                if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, { className: 'CodeMirror-matchingtag' })
+            }
+            var other = match.at == 'close' ? match.open : match.close
+            if (other) cm.state.tagOther = cm.markText(other.from, other.to, { className: 'CodeMirror-matchingtag' })
+            else cm.state.failedTagMatch = true
+        })
+    }
+
+    function maybeUpdateMatch(cm) {
+        if (cm.state.failedTagMatch) doMatchTags(cm)
+    }
+
+    CodeMirror.commands.toMatchingTag = function (cm) {
+        var found = CodeMirror.findMatchingTag(cm, cm.getCursor())
+        if (found) {
+            var other = found.at == 'close' ? found.open : found.close
+            if (other) cm.extendSelection(other.to, other.from)
+        }
+    }
+})

+ 31 - 0
libs/codemirror/addon/edit/trailingspace.js

@@ -0,0 +1,31 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    CodeMirror.defineOption('showTrailingSpace', false, function (cm, val, prev) {
+        if (prev == CodeMirror.Init) prev = false
+        if (prev && !val) cm.removeOverlay('trailingspace')
+        else if (!prev && val)
+            cm.addOverlay({
+                token: function (stream) {
+                    for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
+                    if (i > stream.pos) {
+                        stream.pos = i
+                        return null
+                    }
+                    stream.pos = l
+                    return 'trailingspace'
+                },
+                name: 'trailingspace',
+            })
+    })
+})

+ 149 - 0
libs/codemirror/addon/fold/brace-fold.js

@@ -0,0 +1,149 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    function bracketFolding(pairs) {
+        return function (cm, start) {
+            var line = start.line,
+                lineText = cm.getLine(line)
+
+            function findOpening(pair) {
+                var tokenType
+                for (var at = start.ch, pass = 0; ; ) {
+                    var found = at <= 0 ? -1 : lineText.lastIndexOf(pair[0], at - 1)
+                    if (found == -1) {
+                        if (pass == 1) break
+                        pass = 1
+                        at = lineText.length
+                        continue
+                    }
+                    if (pass == 1 && found < start.ch) break
+                    tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))
+                    if (!/^(comment|string)/.test(tokenType)) return { ch: found + 1, tokenType: tokenType, pair: pair }
+                    at = found - 1
+                }
+            }
+
+            function findRange(found) {
+                var count = 1,
+                    lastLine = cm.lastLine(),
+                    end,
+                    startCh = found.ch,
+                    endCh
+                outer: for (var i = line; i <= lastLine; ++i) {
+                    var text = cm.getLine(i),
+                        pos = i == line ? startCh : 0
+                    for (;;) {
+                        var nextOpen = text.indexOf(found.pair[0], pos),
+                            nextClose = text.indexOf(found.pair[1], pos)
+                        if (nextOpen < 0) nextOpen = text.length
+                        if (nextClose < 0) nextClose = text.length
+                        pos = Math.min(nextOpen, nextClose)
+                        if (pos == text.length) break
+                        if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == found.tokenType) {
+                            if (pos == nextOpen) ++count
+                            else if (!--count) {
+                                end = i
+                                endCh = pos
+                                break outer
+                            }
+                        }
+                        ++pos
+                    }
+                }
+
+                if (end == null || line == end) return null
+                return { from: CodeMirror.Pos(line, startCh), to: CodeMirror.Pos(end, endCh) }
+            }
+
+            var found = []
+            for (var i = 0; i < pairs.length; i++) {
+                var open = findOpening(pairs[i])
+                if (open) found.push(open)
+            }
+            found.sort(function (a, b) {
+                return a.ch - b.ch
+            })
+            for (var i = 0; i < found.length; i++) {
+                var range = findRange(found[i])
+                if (range) return range
+            }
+            return null
+        }
+    }
+
+    CodeMirror.registerHelper(
+        'fold',
+        'brace',
+        bracketFolding([
+            ['{', '}'],
+            ['[', ']'],
+        ])
+    )
+
+    CodeMirror.registerHelper(
+        'fold',
+        'brace-paren',
+        bracketFolding([
+            ['{', '}'],
+            ['[', ']'],
+            ['(', ')'],
+        ])
+    )
+
+    CodeMirror.registerHelper('fold', 'import', function (cm, start) {
+        function hasImport(line) {
+            if (line < cm.firstLine() || line > cm.lastLine()) return null
+            var start = cm.getTokenAt(CodeMirror.Pos(line, 1))
+            if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1))
+            if (start.type != 'keyword' || start.string != 'import') return null
+            // Now find closing semicolon, return its position
+            for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
+                var text = cm.getLine(i),
+                    semi = text.indexOf(';')
+                if (semi != -1) return { startCh: start.end, end: CodeMirror.Pos(i, semi) }
+            }
+        }
+
+        var startLine = start.line,
+            has = hasImport(startLine),
+            prev
+        if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1)) return null
+        for (var end = has.end; ; ) {
+            var next = hasImport(end.line + 1)
+            if (next == null) break
+            end = next.end
+        }
+        return { from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end }
+    })
+
+    CodeMirror.registerHelper('fold', 'include', function (cm, start) {
+        function hasInclude(line) {
+            if (line < cm.firstLine() || line > cm.lastLine()) return null
+            var start = cm.getTokenAt(CodeMirror.Pos(line, 1))
+            if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1))
+            if (start.type == 'meta' && start.string.slice(0, 8) == '#include') return start.start + 8
+        }
+
+        var startLine = start.line,
+            has = hasInclude(startLine)
+        if (has == null || hasInclude(startLine - 1) != null) return null
+        for (var end = startLine; ; ) {
+            var next = hasInclude(end + 1)
+            if (next == null) break
+            ++end
+        }
+        return { from: CodeMirror.Pos(startLine, has + 1), to: cm.clipPos(CodeMirror.Pos(end)) }
+    })
+})

+ 77 - 0
libs/codemirror/addon/fold/comment-fold.js

@@ -0,0 +1,77 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.registerGlobalHelper(
+        'fold',
+        'comment',
+        function (mode) {
+            return mode.blockCommentStart && mode.blockCommentEnd
+        },
+        function (cm, start) {
+            var mode = cm.getModeAt(start),
+                startToken = mode.blockCommentStart,
+                endToken = mode.blockCommentEnd
+            if (!startToken || !endToken) return
+            var line = start.line,
+                lineText = cm.getLine(line)
+
+            var startCh
+            for (var at = start.ch, pass = 0; ; ) {
+                var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1)
+                if (found == -1) {
+                    if (pass == 1) return
+                    pass = 1
+                    at = lineText.length
+                    continue
+                }
+                if (pass == 1 && found < start.ch) return
+                if (
+                    /comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
+                    (found == 0 || lineText.slice(found - endToken.length, found) == endToken || !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))
+                ) {
+                    startCh = found + startToken.length
+                    break
+                }
+                at = found - 1
+            }
+
+            var depth = 1,
+                lastLine = cm.lastLine(),
+                end,
+                endCh
+            outer: for (var i = line; i <= lastLine; ++i) {
+                var text = cm.getLine(i),
+                    pos = i == line ? startCh : 0
+                for (;;) {
+                    var nextOpen = text.indexOf(startToken, pos),
+                        nextClose = text.indexOf(endToken, pos)
+                    if (nextOpen < 0) nextOpen = text.length
+                    if (nextClose < 0) nextClose = text.length
+                    pos = Math.min(nextOpen, nextClose)
+                    if (pos == text.length) break
+                    if (pos == nextOpen) ++depth
+                    else if (!--depth) {
+                        end = i
+                        endCh = pos
+                        break outer
+                    }
+                    ++pos
+                }
+            }
+            if (end == null || (line == end && endCh == startCh)) return
+            return { from: CodeMirror.Pos(line, startCh), to: CodeMirror.Pos(end, endCh) }
+        }
+    )
+})

+ 159 - 0
libs/codemirror/addon/fold/foldcode.js

@@ -0,0 +1,159 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    function doFold(cm, pos, options, force) {
+        if (options && options.call) {
+            var finder = options
+            options = null
+        } else {
+            var finder = getOption(cm, options, 'rangeFinder')
+        }
+        if (typeof pos == 'number') pos = CodeMirror.Pos(pos, 0)
+        var minSize = getOption(cm, options, 'minFoldSize')
+
+        function getRange(allowFolded) {
+            var range = finder(cm, pos)
+            if (!range || range.to.line - range.from.line < minSize) return null
+            if (force === 'fold') return range
+
+            var marks = cm.findMarksAt(range.from)
+            for (var i = 0; i < marks.length; ++i) {
+                if (marks[i].__isFold) {
+                    if (!allowFolded) return null
+                    range.cleared = true
+                    marks[i].clear()
+                }
+            }
+            return range
+        }
+
+        var range = getRange(true)
+        if (getOption(cm, options, 'scanUp'))
+            while (!range && pos.line > cm.firstLine()) {
+                pos = CodeMirror.Pos(pos.line - 1, 0)
+                range = getRange(false)
+            }
+        if (!range || range.cleared || force === 'unfold') return
+
+        var myWidget = makeWidget(cm, options, range)
+        CodeMirror.on(myWidget, 'mousedown', function (e) {
+            myRange.clear()
+            CodeMirror.e_preventDefault(e)
+        })
+        var myRange = cm.markText(range.from, range.to, {
+            replacedWith: myWidget,
+            clearOnEnter: getOption(cm, options, 'clearOnEnter'),
+            __isFold: true,
+        })
+        myRange.on('clear', function (from, to) {
+            CodeMirror.signal(cm, 'unfold', cm, from, to)
+        })
+        CodeMirror.signal(cm, 'fold', cm, range.from, range.to)
+    }
+
+    function makeWidget(cm, options, range) {
+        var widget = getOption(cm, options, 'widget')
+
+        if (typeof widget == 'function') {
+            widget = widget(range.from, range.to)
+        }
+
+        if (typeof widget == 'string') {
+            var text = document.createTextNode(widget)
+            widget = document.createElement('span')
+            widget.appendChild(text)
+            widget.className = 'CodeMirror-foldmarker'
+        } else if (widget) {
+            widget = widget.cloneNode(true)
+        }
+        return widget
+    }
+
+    // Clumsy backwards-compatible interface
+    CodeMirror.newFoldFunction = function (rangeFinder, widget) {
+        return function (cm, pos) {
+            doFold(cm, pos, { rangeFinder: rangeFinder, widget: widget })
+        }
+    }
+
+    // New-style interface
+    CodeMirror.defineExtension('foldCode', function (pos, options, force) {
+        doFold(this, pos, options, force)
+    })
+
+    CodeMirror.defineExtension('isFolded', function (pos) {
+        var marks = this.findMarksAt(pos)
+        for (var i = 0; i < marks.length; ++i) if (marks[i].__isFold) return true
+    })
+
+    CodeMirror.commands.toggleFold = function (cm) {
+        cm.foldCode(cm.getCursor())
+    }
+    CodeMirror.commands.fold = function (cm) {
+        cm.foldCode(cm.getCursor(), null, 'fold')
+    }
+    CodeMirror.commands.unfold = function (cm) {
+        cm.foldCode(cm.getCursor(), { scanUp: false }, 'unfold')
+    }
+    CodeMirror.commands.foldAll = function (cm) {
+        cm.operation(function () {
+            for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, 'fold')
+        })
+    }
+    CodeMirror.commands.unfoldAll = function (cm) {
+        cm.operation(function () {
+            for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++) cm.foldCode(CodeMirror.Pos(i, 0), { scanUp: false }, 'unfold')
+        })
+    }
+
+    CodeMirror.registerHelper('fold', 'combine', function () {
+        var funcs = Array.prototype.slice.call(arguments, 0)
+        return function (cm, start) {
+            for (var i = 0; i < funcs.length; ++i) {
+                var found = funcs[i](cm, start)
+                if (found) return found
+            }
+        }
+    })
+
+    CodeMirror.registerHelper('fold', 'auto', function (cm, start) {
+        var helpers = cm.getHelpers(start, 'fold')
+        for (var i = 0; i < helpers.length; i++) {
+            var cur = helpers[i](cm, start)
+            if (cur) return cur
+        }
+    })
+
+    var defaultOptions = {
+        rangeFinder: CodeMirror.fold.auto,
+        widget: '\u2194',
+        minFoldSize: 0,
+        scanUp: false,
+        clearOnEnter: true,
+    }
+
+    CodeMirror.defineOption('foldOptions', null)
+
+    function getOption(cm, options, name) {
+        if (options && options[name] !== undefined) return options[name]
+        var editorOptions = cm.options.foldOptions
+        if (editorOptions && editorOptions[name] !== undefined) return editorOptions[name]
+        return defaultOptions[name]
+    }
+
+    CodeMirror.defineExtension('foldOption', function (options, name) {
+        return getOption(this, options, name)
+    })
+})

+ 20 - 0
libs/codemirror/addon/fold/foldgutter.css

@@ -0,0 +1,20 @@
+.CodeMirror-foldmarker {
+  color: blue;
+  text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
+  font-family: arial;
+  line-height: .3;
+  cursor: pointer;
+}
+.CodeMirror-foldgutter {
+  width: .7em;
+}
+.CodeMirror-foldgutter-open,
+.CodeMirror-foldgutter-folded {
+  cursor: pointer;
+}
+.CodeMirror-foldgutter-open:after {
+  content: "\25BE";
+}
+.CodeMirror-foldgutter-folded:after {
+  content: "\25B8";
+}

+ 170 - 0
libs/codemirror/addon/fold/foldgutter.js

@@ -0,0 +1,170 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('./foldcode'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', './foldcode'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineOption('foldGutter', false, function (cm, val, old) {
+        if (old && old != CodeMirror.Init) {
+            cm.clearGutter(cm.state.foldGutter.options.gutter)
+            cm.state.foldGutter = null
+            cm.off('gutterClick', onGutterClick)
+            cm.off('changes', onChange)
+            cm.off('viewportChange', onViewportChange)
+            cm.off('fold', onFold)
+            cm.off('unfold', onFold)
+            cm.off('swapDoc', onChange)
+        }
+        if (val) {
+            cm.state.foldGutter = new State(parseOptions(val))
+            updateInViewport(cm)
+            cm.on('gutterClick', onGutterClick)
+            cm.on('changes', onChange)
+            cm.on('viewportChange', onViewportChange)
+            cm.on('fold', onFold)
+            cm.on('unfold', onFold)
+            cm.on('swapDoc', onChange)
+        }
+    })
+
+    var Pos = CodeMirror.Pos
+
+    function State(options) {
+        this.options = options
+        this.from = this.to = 0
+    }
+
+    function parseOptions(opts) {
+        if (opts === true) opts = {}
+        if (opts.gutter == null) opts.gutter = 'CodeMirror-foldgutter'
+        if (opts.indicatorOpen == null) opts.indicatorOpen = 'CodeMirror-foldgutter-open'
+        if (opts.indicatorFolded == null) opts.indicatorFolded = 'CodeMirror-foldgutter-folded'
+        return opts
+    }
+
+    function isFolded(cm, line) {
+        var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0))
+        for (var i = 0; i < marks.length; ++i) {
+            if (marks[i].__isFold) {
+                var fromPos = marks[i].find(-1)
+                if (fromPos && fromPos.line === line) return marks[i]
+            }
+        }
+    }
+
+    function marker(spec) {
+        if (typeof spec == 'string') {
+            var elt = document.createElement('div')
+            elt.className = spec + ' CodeMirror-guttermarker-subtle'
+            return elt
+        } else {
+            return spec.cloneNode(true)
+        }
+    }
+
+    function updateFoldInfo(cm, from, to) {
+        var opts = cm.state.foldGutter.options,
+            cur = from - 1
+        var minSize = cm.foldOption(opts, 'minFoldSize')
+        var func = cm.foldOption(opts, 'rangeFinder')
+        // we can reuse the built-in indicator element if its className matches the new state
+        var clsFolded = typeof opts.indicatorFolded == 'string' && classTest(opts.indicatorFolded)
+        var clsOpen = typeof opts.indicatorOpen == 'string' && classTest(opts.indicatorOpen)
+        cm.eachLine(from, to, function (line) {
+            ++cur
+            var mark = null
+            var old = line.gutterMarkers
+            if (old) old = old[opts.gutter]
+            if (isFolded(cm, cur)) {
+                if (clsFolded && old && clsFolded.test(old.className)) return
+                mark = marker(opts.indicatorFolded)
+            } else {
+                var pos = Pos(cur, 0)
+                var range = func && func(cm, pos)
+                if (range && range.to.line - range.from.line >= minSize) {
+                    if (clsOpen && old && clsOpen.test(old.className)) return
+                    mark = marker(opts.indicatorOpen)
+                }
+            }
+            if (!mark && !old) return
+            cm.setGutterMarker(line, opts.gutter, mark)
+        })
+    }
+
+    // copied from CodeMirror/src/util/dom.js
+    function classTest(cls) {
+        return new RegExp('(^|\\s)' + cls + '(?:$|\\s)\\s*')
+    }
+
+    function updateInViewport(cm) {
+        var vp = cm.getViewport(),
+            state = cm.state.foldGutter
+        if (!state) return
+        cm.operation(function () {
+            updateFoldInfo(cm, vp.from, vp.to)
+        })
+        state.from = vp.from
+        state.to = vp.to
+    }
+
+    function onGutterClick(cm, line, gutter) {
+        var state = cm.state.foldGutter
+        if (!state) return
+        var opts = state.options
+        if (gutter != opts.gutter) return
+        var folded = isFolded(cm, line)
+        if (folded) folded.clear()
+        else cm.foldCode(Pos(line, 0), opts)
+    }
+
+    function onChange(cm) {
+        var state = cm.state.foldGutter
+        if (!state) return
+        var opts = state.options
+        state.from = state.to = 0
+        clearTimeout(state.changeUpdate)
+        state.changeUpdate = setTimeout(function () {
+            updateInViewport(cm)
+        }, opts.foldOnChangeTimeSpan || 600)
+    }
+
+    function onViewportChange(cm) {
+        var state = cm.state.foldGutter
+        if (!state) return
+        var opts = state.options
+        clearTimeout(state.changeUpdate)
+        state.changeUpdate = setTimeout(function () {
+            var vp = cm.getViewport()
+            if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
+                updateInViewport(cm)
+            } else {
+                cm.operation(function () {
+                    if (vp.from < state.from) {
+                        updateFoldInfo(cm, vp.from, state.from)
+                        state.from = vp.from
+                    }
+                    if (vp.to > state.to) {
+                        updateFoldInfo(cm, state.to, vp.to)
+                        state.to = vp.to
+                    }
+                })
+            }
+        }, opts.updateViewportTimeSpan || 400)
+    }
+
+    function onFold(cm, from) {
+        var state = cm.state.foldGutter
+        if (!state) return
+        var line = from.line
+        if (line >= state.from && line < state.to) updateFoldInfo(cm, line, line + 1)
+    }
+})

+ 49 - 0
libs/codemirror/addon/fold/indent-fold.js

@@ -0,0 +1,49 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    function lineIndent(cm, lineNo) {
+        var text = cm.getLine(lineNo)
+        var spaceTo = text.search(/\S/)
+        if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1)))) return -1
+        return CodeMirror.countColumn(text, null, cm.getOption('tabSize'))
+    }
+
+    CodeMirror.registerHelper('fold', 'indent', function (cm, start) {
+        var myIndent = lineIndent(cm, start.line)
+        if (myIndent < 0) return
+        var lastLineInFold = null
+
+        // Go through lines until we find a line that definitely doesn't belong in
+        // the block we're folding, or to the end.
+        for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
+            var indent = lineIndent(cm, i)
+            if (indent == -1) {
+            } else if (indent > myIndent) {
+                // Lines with a greater indent are considered part of the block.
+                lastLineInFold = i
+            } else {
+                // If this line has non-space, non-comment content, and is
+                // indented less or equal to the start line, it is the start of
+                // another block.
+                break
+            }
+        }
+        if (lastLineInFold)
+            return {
+                from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
+                to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length),
+            }
+    })
+})

+ 52 - 0
libs/codemirror/addon/fold/markdown-fold.js

@@ -0,0 +1,52 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.registerHelper('fold', 'markdown', function (cm, start) {
+        var maxDepth = 100
+
+        function isHeader(lineNo) {
+            var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0))
+            return tokentype && /\bheader\b/.test(tokentype)
+        }
+
+        function headerLevel(lineNo, line, nextLine) {
+            var match = line && line.match(/^#+/)
+            if (match && isHeader(lineNo)) return match[0].length
+            match = nextLine && nextLine.match(/^[=\-]+\s*$/)
+            if (match && isHeader(lineNo + 1)) return nextLine[0] == '=' ? 1 : 2
+            return maxDepth
+        }
+
+        var firstLine = cm.getLine(start.line),
+            nextLine = cm.getLine(start.line + 1)
+        var level = headerLevel(start.line, firstLine, nextLine)
+        if (level === maxDepth) return undefined
+
+        var lastLineNo = cm.lastLine()
+        var end = start.line,
+            nextNextLine = cm.getLine(end + 2)
+        while (end < lastLineNo) {
+            if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break
+            ++end
+            nextLine = nextNextLine
+            nextNextLine = cm.getLine(end + 2)
+        }
+
+        return {
+            from: CodeMirror.Pos(start.line, firstLine.length),
+            to: CodeMirror.Pos(end, cm.getLine(end).length),
+        }
+    })
+})

+ 232 - 0
libs/codemirror/addon/fold/xml-fold.js

@@ -0,0 +1,232 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var Pos = CodeMirror.Pos
+    function cmp(a, b) {
+        return a.line - b.line || a.ch - b.ch
+    }
+
+    var nameStartChar = 'A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD'
+    var nameChar = nameStartChar + '-:.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040'
+    var xmlTagStart = new RegExp('<(/?)([' + nameStartChar + '][' + nameChar + ']*)', 'g')
+
+    function Iter(cm, line, ch, range) {
+        this.line = line
+        this.ch = ch
+        this.cm = cm
+        this.text = cm.getLine(line)
+        this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine()
+        this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine()
+    }
+
+    function tagAt(iter, ch) {
+        var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch))
+        return type && /\btag\b/.test(type)
+    }
+
+    function nextLine(iter) {
+        if (iter.line >= iter.max) return
+        iter.ch = 0
+        iter.text = iter.cm.getLine(++iter.line)
+        return true
+    }
+    function prevLine(iter) {
+        if (iter.line <= iter.min) return
+        iter.text = iter.cm.getLine(--iter.line)
+        iter.ch = iter.text.length
+        return true
+    }
+
+    function toTagEnd(iter) {
+        for (;;) {
+            var gt = iter.text.indexOf('>', iter.ch)
+            if (gt == -1) {
+                if (nextLine(iter)) continue
+                else return
+            }
+            if (!tagAt(iter, gt + 1)) {
+                iter.ch = gt + 1
+                continue
+            }
+            var lastSlash = iter.text.lastIndexOf('/', gt)
+            var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt))
+            iter.ch = gt + 1
+            return selfClose ? 'selfClose' : 'regular'
+        }
+    }
+    function toTagStart(iter) {
+        for (;;) {
+            var lt = iter.ch ? iter.text.lastIndexOf('<', iter.ch - 1) : -1
+            if (lt == -1) {
+                if (prevLine(iter)) continue
+                else return
+            }
+            if (!tagAt(iter, lt + 1)) {
+                iter.ch = lt
+                continue
+            }
+            xmlTagStart.lastIndex = lt
+            iter.ch = lt
+            var match = xmlTagStart.exec(iter.text)
+            if (match && match.index == lt) return match
+        }
+    }
+
+    function toNextTag(iter) {
+        for (;;) {
+            xmlTagStart.lastIndex = iter.ch
+            var found = xmlTagStart.exec(iter.text)
+            if (!found) {
+                if (nextLine(iter)) continue
+                else return
+            }
+            if (!tagAt(iter, found.index + 1)) {
+                iter.ch = found.index + 1
+                continue
+            }
+            iter.ch = found.index + found[0].length
+            return found
+        }
+    }
+    function toPrevTag(iter) {
+        for (;;) {
+            var gt = iter.ch ? iter.text.lastIndexOf('>', iter.ch - 1) : -1
+            if (gt == -1) {
+                if (prevLine(iter)) continue
+                else return
+            }
+            if (!tagAt(iter, gt + 1)) {
+                iter.ch = gt
+                continue
+            }
+            var lastSlash = iter.text.lastIndexOf('/', gt)
+            var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt))
+            iter.ch = gt + 1
+            return selfClose ? 'selfClose' : 'regular'
+        }
+    }
+
+    function findMatchingClose(iter, tag) {
+        var stack = []
+        for (;;) {
+            var next = toNextTag(iter),
+                end,
+                startLine = iter.line,
+                startCh = iter.ch - (next ? next[0].length : 0)
+            if (!next || !(end = toTagEnd(iter))) return
+            if (end == 'selfClose') continue
+            if (next[1]) {
+                // closing tag
+                for (var i = stack.length - 1; i >= 0; --i)
+                    if (stack[i] == next[2]) {
+                        stack.length = i
+                        break
+                    }
+                if (i < 0 && (!tag || tag == next[2]))
+                    return {
+                        tag: next[2],
+                        from: Pos(startLine, startCh),
+                        to: Pos(iter.line, iter.ch),
+                    }
+            } else {
+                // opening tag
+                stack.push(next[2])
+            }
+        }
+    }
+    function findMatchingOpen(iter, tag) {
+        var stack = []
+        for (;;) {
+            var prev = toPrevTag(iter)
+            if (!prev) return
+            if (prev == 'selfClose') {
+                toTagStart(iter)
+                continue
+            }
+            var endLine = iter.line,
+                endCh = iter.ch
+            var start = toTagStart(iter)
+            if (!start) return
+            if (start[1]) {
+                // closing tag
+                stack.push(start[2])
+            } else {
+                // opening tag
+                for (var i = stack.length - 1; i >= 0; --i)
+                    if (stack[i] == start[2]) {
+                        stack.length = i
+                        break
+                    }
+                if (i < 0 && (!tag || tag == start[2]))
+                    return {
+                        tag: start[2],
+                        from: Pos(iter.line, iter.ch),
+                        to: Pos(endLine, endCh),
+                    }
+            }
+        }
+    }
+
+    CodeMirror.registerHelper('fold', 'xml', function (cm, start) {
+        var iter = new Iter(cm, start.line, 0)
+        for (;;) {
+            var openTag = toNextTag(iter)
+            if (!openTag || iter.line != start.line) return
+            var end = toTagEnd(iter)
+            if (!end) return
+            if (!openTag[1] && end != 'selfClose') {
+                var startPos = Pos(iter.line, iter.ch)
+                var endPos = findMatchingClose(iter, openTag[2])
+                return endPos && cmp(endPos.from, startPos) > 0 ? { from: startPos, to: endPos.from } : null
+            }
+        }
+    })
+    CodeMirror.findMatchingTag = function (cm, pos, range) {
+        var iter = new Iter(cm, pos.line, pos.ch, range)
+        if (iter.text.indexOf('>') == -1 && iter.text.indexOf('<') == -1) return
+        var end = toTagEnd(iter),
+            to = end && Pos(iter.line, iter.ch)
+        var start = end && toTagStart(iter)
+        if (!end || !start || cmp(iter, pos) > 0) return
+        var here = { from: Pos(iter.line, iter.ch), to: to, tag: start[2] }
+        if (end == 'selfClose') return { open: here, close: null, at: 'open' }
+
+        if (start[1]) {
+            // closing tag
+            return { open: findMatchingOpen(iter, start[2]), close: here, at: 'close' }
+        } else {
+            // opening tag
+            iter = new Iter(cm, to.line, to.ch, range)
+            return { open: here, close: findMatchingClose(iter, start[2]), at: 'open' }
+        }
+    }
+
+    CodeMirror.findEnclosingTag = function (cm, pos, range, tag) {
+        var iter = new Iter(cm, pos.line, pos.ch, range)
+        for (;;) {
+            var open = findMatchingOpen(iter, tag)
+            if (!open) break
+            var forward = new Iter(cm, pos.line, pos.ch, range)
+            var close = findMatchingClose(forward, open.tag)
+            if (close) return { open: open, close: close }
+        }
+    }
+
+    // Used by addon/edit/closetag.js
+    CodeMirror.scanForClosingTag = function (cm, pos, name, end) {
+        var iter = new Iter(cm, pos.line, pos.ch, end ? { from: 0, to: end } : null)
+        return findMatchingClose(iter, name)
+    }
+})

+ 49 - 0
libs/codemirror/addon/hint/anyword-hint.js

@@ -0,0 +1,49 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var WORD = /[\w$]+/,
+        RANGE = 500
+
+    CodeMirror.registerHelper('hint', 'anyword', function (editor, options) {
+        var word = (options && options.word) || WORD
+        var range = (options && options.range) || RANGE
+        var cur = editor.getCursor(),
+            curLine = editor.getLine(cur.line)
+        var end = cur.ch,
+            start = end
+        while (start && word.test(curLine.charAt(start - 1))) --start
+        var curWord = start != end && curLine.slice(start, end)
+
+        var list = (options && options.list) || [],
+            seen = {}
+        var re = new RegExp(word.source, 'g')
+        for (var dir = -1; dir <= 1; dir += 2) {
+            var line = cur.line,
+                endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir
+            for (; line != endLine; line += dir) {
+                var text = editor.getLine(line),
+                    m
+                while ((m = re.exec(text))) {
+                    if (line == cur.line && m[0] === curWord) continue
+                    if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
+                        seen[m[0]] = true
+                        list.push(m[0])
+                    }
+                }
+            }
+        }
+        return { list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end) }
+    })
+})

+ 101 - 0
libs/codemirror/addon/hint/css-hint.js

@@ -0,0 +1,101 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('../../mode/css/css'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', '../../mode/css/css'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var pseudoClasses = {
+        active: 1,
+        after: 1,
+        before: 1,
+        checked: 1,
+        default: 1,
+        disabled: 1,
+        empty: 1,
+        enabled: 1,
+        'first-child': 1,
+        'first-letter': 1,
+        'first-line': 1,
+        'first-of-type': 1,
+        focus: 1,
+        hover: 1,
+        'in-range': 1,
+        indeterminate: 1,
+        invalid: 1,
+        lang: 1,
+        'last-child': 1,
+        'last-of-type': 1,
+        link: 1,
+        not: 1,
+        'nth-child': 1,
+        'nth-last-child': 1,
+        'nth-last-of-type': 1,
+        'nth-of-type': 1,
+        'only-of-type': 1,
+        'only-child': 1,
+        optional: 1,
+        'out-of-range': 1,
+        placeholder: 1,
+        'read-only': 1,
+        'read-write': 1,
+        required: 1,
+        root: 1,
+        selection: 1,
+        target: 1,
+        valid: 1,
+        visited: 1,
+    }
+
+    CodeMirror.registerHelper('hint', 'css', function (cm) {
+        var cur = cm.getCursor(),
+            token = cm.getTokenAt(cur)
+        var inner = CodeMirror.innerMode(cm.getMode(), token.state)
+        if (inner.mode.name != 'css') return
+
+        if (token.type == 'keyword' && '!important'.indexOf(token.string) == 0) return { list: ['!important'], from: CodeMirror.Pos(cur.line, token.start), to: CodeMirror.Pos(cur.line, token.end) }
+
+        var start = token.start,
+            end = cur.ch,
+            word = token.string.slice(0, end - start)
+        if (/[^\w$_-]/.test(word)) {
+            word = ''
+            start = end = cur.ch
+        }
+
+        var spec = CodeMirror.resolveMode('text/css')
+
+        var result = []
+        function add(keywords) {
+            for (var name in keywords) if (!word || name.lastIndexOf(word, 0) == 0) result.push(name)
+        }
+
+        var st = inner.state.state
+        if (st == 'pseudo' || token.type == 'variable-3') {
+            add(pseudoClasses)
+        } else if (st == 'block' || st == 'maybeprop') {
+            add(spec.propertyKeywords)
+        } else if (st == 'prop' || st == 'parens' || st == 'at' || st == 'params') {
+            add(spec.valueKeywords)
+            add(spec.colorKeywords)
+        } else if (st == 'media' || st == 'media_parens') {
+            add(spec.mediaTypes)
+            add(spec.mediaFeatures)
+        }
+
+        if (result.length)
+            return {
+                list: result,
+                from: CodeMirror.Pos(cur.line, start),
+                to: CodeMirror.Pos(cur.line, end),
+            }
+    })
+})

Dosya farkı çok büyük olduğundan ihmal edildi
+ 459 - 0
libs/codemirror/addon/hint/html-hint.js


+ 160 - 0
libs/codemirror/addon/hint/javascript-hint.js

@@ -0,0 +1,160 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    var Pos = CodeMirror.Pos
+
+    function forEach(arr, f) {
+        for (var i = 0, e = arr.length; i < e; ++i) f(arr[i])
+    }
+
+    function arrayContains(arr, item) {
+        if (!Array.prototype.indexOf) {
+            var i = arr.length
+            while (i--) {
+                if (arr[i] === item) {
+                    return true
+                }
+            }
+            return false
+        }
+        return arr.indexOf(item) != -1
+    }
+
+    function scriptHint(editor, keywords, getToken, options) {
+        // Find the token at the cursor
+        var cur = editor.getCursor(),
+            token = getToken(editor, cur)
+        if (/\b(?:string|comment)\b/.test(token.type)) return
+        var innerMode = CodeMirror.innerMode(editor.getMode(), token.state)
+        if (innerMode.mode.helperType === 'json') return
+        token.state = innerMode.state
+
+        // If it's not a 'word-style' token, ignore the token.
+        if (!/^[\w$_]*$/.test(token.string)) {
+            token = { start: cur.ch, end: cur.ch, string: '', state: token.state, type: token.string == '.' ? 'property' : null }
+        } else if (token.end > cur.ch) {
+            token.end = cur.ch
+            token.string = token.string.slice(0, cur.ch - token.start)
+        }
+
+        var tprop = token
+        // If it is a property, find out what it is a property of.
+        while (tprop.type == 'property') {
+            tprop = getToken(editor, Pos(cur.line, tprop.start))
+            if (tprop.string != '.') return
+            tprop = getToken(editor, Pos(cur.line, tprop.start))
+            if (!context) var context = []
+            context.push(tprop)
+        }
+        return { list: getCompletions(token, context, keywords, options), from: Pos(cur.line, token.start), to: Pos(cur.line, token.end) }
+    }
+
+    function javascriptHint(editor, options) {
+        return scriptHint(
+            editor,
+            javascriptKeywords,
+            function (e, cur) {
+                return e.getTokenAt(cur)
+            },
+            options
+        )
+    }
+    CodeMirror.registerHelper('hint', 'javascript', javascriptHint)
+
+    function getCoffeeScriptToken(editor, cur) {
+        // This getToken, it is for coffeescript, imitates the behavior of
+        // getTokenAt method in javascript.js, that is, returning "property"
+        // type and treat "." as independent token.
+        var token = editor.getTokenAt(cur)
+        if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
+            token.end = token.start
+            token.string = '.'
+            token.type = 'property'
+        } else if (/^\.[\w$_]*$/.test(token.string)) {
+            token.type = 'property'
+            token.start++
+            token.string = token.string.replace(/\./, '')
+        }
+        return token
+    }
+
+    function coffeescriptHint(editor, options) {
+        return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options)
+    }
+    CodeMirror.registerHelper('hint', 'coffeescript', coffeescriptHint)
+
+    var stringProps = ('charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight ' + 'toUpperCase toLowerCase split concat match replace search').split(' ')
+    var arrayProps = ('length concat join splice push pop shift unshift slice reverse sort indexOf ' + 'lastIndexOf every some filter forEach map reduce reduceRight ').split(' ')
+    var funcProps = 'prototype apply call bind'.split(' ')
+    var javascriptKeywords = (
+        'break case catch class const continue debugger default delete do else export extends false finally for function ' +
+        'if in import instanceof new null return super switch this throw true try typeof var void while with yield'
+    ).split(' ')
+    var coffeescriptKeywords = (
+        'and break catch class continue delete do else extends false finally for ' +
+        'if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes'
+    ).split(' ')
+
+    function forAllProps(obj, callback) {
+        if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
+            for (var name in obj) callback(name)
+        } else {
+            for (var o = obj; o; o = Object.getPrototypeOf(o)) Object.getOwnPropertyNames(o).forEach(callback)
+        }
+    }
+
+    function getCompletions(token, context, keywords, options) {
+        var found = [],
+            start = token.string,
+            global = (options && options.globalScope) || window
+        function maybeAdd(str) {
+            if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str)
+        }
+        function gatherCompletions(obj) {
+            if (typeof obj == 'string') forEach(stringProps, maybeAdd)
+            else if (obj instanceof Array) forEach(arrayProps, maybeAdd)
+            else if (obj instanceof Function) forEach(funcProps, maybeAdd)
+            forAllProps(obj, maybeAdd)
+        }
+
+        if (context && context.length) {
+            // If this is a property, see if it belongs to some object we can
+            // find in the current environment.
+            var obj = context.pop(),
+                base
+            if (obj.type && obj.type.indexOf('variable') === 0) {
+                if (options && options.additionalContext) base = options.additionalContext[obj.string]
+                if (!options || options.useGlobalScope !== false) base = base || global[obj.string]
+            } else if (obj.type == 'string') {
+                base = ''
+            } else if (obj.type == 'atom') {
+                base = 1
+            } else if (obj.type == 'function') {
+                if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') && typeof global.jQuery == 'function') base = global.jQuery()
+                else if (global._ != null && obj.string == '_' && typeof global._ == 'function') base = global._()
+            }
+            while (base != null && context.length) base = base[context.pop().string]
+            if (base != null) gatherCompletions(base)
+        } else {
+            // If not, just look in the global object, any local scope, and optional additional-context
+            // (reading into JS mode internals to get at the local and global variables)
+            for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name)
+            for (var c = token.state.context; c; c = c.prev) for (var v = c.vars; v; v = v.next) maybeAdd(v.name)
+            for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name)
+            if (options && options.additionalContext != null) for (var key in options.additionalContext) maybeAdd(key)
+            if (!options || options.useGlobalScope !== false) gatherCompletions(global)
+            forEach(keywords, maybeAdd)
+        }
+        return found
+    }
+})

+ 36 - 0
libs/codemirror/addon/hint/show-hint.css

@@ -0,0 +1,36 @@
+.CodeMirror-hints {
+  position: absolute;
+  z-index: 10;
+  overflow: hidden;
+  list-style: none;
+
+  margin: 0;
+  padding: 2px;
+
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  border-radius: 3px;
+  border: 1px solid silver;
+
+  background: white;
+  font-size: 90%;
+  font-family: monospace;
+
+  max-height: 20em;
+  overflow-y: auto;
+}
+
+.CodeMirror-hint {
+  margin: 0;
+  padding: 0 4px;
+  border-radius: 2px;
+  white-space: pre;
+  color: black;
+  cursor: pointer;
+}
+
+li.CodeMirror-hint-active {
+  background: #08f;
+  color: white;
+}

+ 602 - 0
libs/codemirror/addon/hint/show-hint.js

@@ -0,0 +1,602 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// declare global: DOMRect
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var HINT_ELEMENT_CLASS = 'CodeMirror-hint'
+    var ACTIVE_HINT_ELEMENT_CLASS = 'CodeMirror-hint-active'
+
+    // This is the old interface, kept around for now to stay
+    // backwards-compatible.
+    CodeMirror.showHint = function (cm, getHints, options) {
+        if (!getHints) return cm.showHint(options)
+        if (options && options.async) getHints.async = true
+        var newOpts = { hint: getHints }
+        if (options) for (var prop in options) newOpts[prop] = options[prop]
+        return cm.showHint(newOpts)
+    }
+
+    CodeMirror.defineExtension('showHint', function (options) {
+        options = parseOptions(this, this.getCursor('start'), options)
+        var selections = this.listSelections()
+        if (selections.length > 1) return
+        // By default, don't allow completion when something is selected.
+        // A hint function can have a `supportsSelection` property to
+        // indicate that it can handle selections.
+        if (this.somethingSelected()) {
+            if (!options.hint.supportsSelection) return
+            // Don't try with cross-line selections
+            for (var i = 0; i < selections.length; i++) if (selections[i].head.line != selections[i].anchor.line) return
+        }
+
+        if (this.state.completionActive) this.state.completionActive.close()
+        var completion = (this.state.completionActive = new Completion(this, options))
+        if (!completion.options.hint) return
+
+        CodeMirror.signal(this, 'startCompletion', this)
+        completion.update(true)
+    })
+
+    CodeMirror.defineExtension('closeHint', function () {
+        if (this.state.completionActive) this.state.completionActive.close()
+    })
+
+    function Completion(cm, options) {
+        this.cm = cm
+        this.options = options
+        this.widget = null
+        this.debounce = 0
+        this.tick = 0
+        this.startPos = this.cm.getCursor('start')
+        this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length
+
+        if (this.options.updateOnCursorActivity) {
+            var self = this
+            cm.on(
+                'cursorActivity',
+                (this.activityFunc = function () {
+                    self.cursorActivity()
+                })
+            )
+        }
+    }
+
+    var requestAnimationFrame =
+        window.requestAnimationFrame ||
+        function (fn) {
+            return setTimeout(fn, 1000 / 60)
+        }
+    var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout
+
+    Completion.prototype = {
+        close: function () {
+            if (!this.active()) return
+            this.cm.state.completionActive = null
+            this.tick = null
+            if (this.options.updateOnCursorActivity) {
+                this.cm.off('cursorActivity', this.activityFunc)
+            }
+
+            if (this.widget && this.data) CodeMirror.signal(this.data, 'close')
+            if (this.widget) this.widget.close()
+            CodeMirror.signal(this.cm, 'endCompletion', this.cm)
+        },
+
+        active: function () {
+            return this.cm.state.completionActive == this
+        },
+
+        pick: function (data, i) {
+            var completion = data.list[i],
+                self = this
+            this.cm.operation(function () {
+                if (completion.hint) completion.hint(self.cm, data, completion)
+                else self.cm.replaceRange(getText(completion), completion.from || data.from, completion.to || data.to, 'complete')
+                CodeMirror.signal(data, 'pick', completion)
+                self.cm.scrollIntoView()
+            })
+            if (this.options.closeOnPick) {
+                this.close()
+            }
+        },
+
+        cursorActivity: function () {
+            if (this.debounce) {
+                cancelAnimationFrame(this.debounce)
+                this.debounce = 0
+            }
+
+            var identStart = this.startPos
+            if (this.data) {
+                identStart = this.data.from
+            }
+
+            var pos = this.cm.getCursor(),
+                line = this.cm.getLine(pos.line)
+            if (
+                pos.line != this.startPos.line ||
+                line.length - pos.ch != this.startLen - this.startPos.ch ||
+                pos.ch < identStart.ch ||
+                this.cm.somethingSelected() ||
+                !pos.ch ||
+                this.options.closeCharacters.test(line.charAt(pos.ch - 1))
+            ) {
+                this.close()
+            } else {
+                var self = this
+                this.debounce = requestAnimationFrame(function () {
+                    self.update()
+                })
+                if (this.widget) this.widget.disable()
+            }
+        },
+
+        update: function (first) {
+            if (this.tick == null) return
+            var self = this,
+                myTick = ++this.tick
+            fetchHints(this.options.hint, this.cm, this.options, function (data) {
+                if (self.tick == myTick) self.finishUpdate(data, first)
+            })
+        },
+
+        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()
+
+            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')
+                }
+            }
+        },
+    }
+
+    function parseOptions(cm, pos, options) {
+        var editor = cm.options.hintOptions
+        var out = {}
+        for (var prop in defaultOptions) out[prop] = defaultOptions[prop]
+        if (editor) for (var prop in editor) if (editor[prop] !== undefined) out[prop] = editor[prop]
+        if (options) for (var prop in options) if (options[prop] !== undefined) out[prop] = options[prop]
+        if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
+        return out
+    }
+
+    function getText(completion) {
+        if (typeof completion == 'string') return completion
+        else return completion.text
+    }
+
+    function buildKeyMap(completion, handle) {
+        var baseMap = {
+            Up: function () {
+                handle.moveFocus(-1)
+            },
+            Down: function () {
+                handle.moveFocus(1)
+            },
+            PageUp: function () {
+                handle.moveFocus(-handle.menuSize() + 1, true)
+            },
+            PageDown: function () {
+                handle.moveFocus(handle.menuSize() - 1, true)
+            },
+            Home: function () {
+                handle.setFocus(0)
+            },
+            End: function () {
+                handle.setFocus(handle.length - 1)
+            },
+            Enter: handle.pick,
+            Tab: handle.pick,
+            Esc: handle.close,
+        }
+
+        var mac = /Mac/.test(navigator.platform)
+
+        if (mac) {
+            baseMap['Ctrl-P'] = function () {
+                handle.moveFocus(-1)
+            }
+            baseMap['Ctrl-N'] = function () {
+                handle.moveFocus(1)
+            }
+        }
+
+        var custom = completion.options.customKeys
+        var ourMap = custom ? {} : baseMap
+        function addBinding(key, val) {
+            var bound
+            if (typeof val != 'string')
+                bound = function (cm) {
+                    return val(cm, handle)
+                }
+            // This mechanism is deprecated
+            else if (baseMap.hasOwnProperty(val)) bound = baseMap[val]
+            else bound = val
+            ourMap[key] = bound
+        }
+        if (custom) for (var key in custom) if (custom.hasOwnProperty(key)) addBinding(key, custom[key])
+        var extra = completion.options.extraKeys
+        if (extra) for (var key in extra) if (extra.hasOwnProperty(key)) addBinding(key, extra[key])
+        return ourMap
+    }
+
+    function getHintElement(hintsElement, el) {
+        while (el && el != hintsElement) {
+            if (el.nodeName.toUpperCase() === 'LI' && el.parentNode == hintsElement) return el
+            el = el.parentNode
+        }
+    }
+
+    function Widget(completion, data) {
+        this.id = 'cm-complete-' + Math.floor(Math.random(1e6))
+        this.completion = completion
+        this.data = data
+        this.picked = false
+        var widget = this,
+            cm = completion.cm
+        var ownerDocument = cm.getInputField().ownerDocument
+        var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow
+
+        var hints = (this.hints = ownerDocument.createElement('ul'))
+        hints.setAttribute('role', 'listbox')
+        hints.setAttribute('aria-expanded', 'true')
+        hints.id = this.id
+        var theme = completion.cm.options.theme
+        hints.className = 'CodeMirror-hints ' + theme
+        this.selectedHint = data.selectedHint || 0
+
+        var completions = data.list
+        for (var i = 0; i < completions.length; ++i) {
+            var elt = hints.appendChild(ownerDocument.createElement('li')),
+                cur = completions[i]
+            var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? '' : ' ' + ACTIVE_HINT_ELEMENT_CLASS)
+            if (cur.className != null) className = cur.className + ' ' + className
+            elt.className = className
+            if (i == this.selectedHint) elt.setAttribute('aria-selected', 'true')
+            elt.id = this.id + '-' + i
+            elt.setAttribute('role', 'option')
+            if (cur.render) cur.render(elt, data, cur)
+            else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)))
+            elt.hintId = i
+        }
+
+        var container = completion.options.container || ownerDocument.body
+        var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null)
+        var left = pos.left,
+            top = pos.bottom,
+            below = true
+        var offsetLeft = 0,
+            offsetTop = 0
+        if (container !== ownerDocument.body) {
+            // We offset the cursor position because left and top are relative to the offsetParent's top left corner.
+            var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1
+            var offsetParent = isContainerPositioned ? container : container.offsetParent
+            var offsetParentPosition = offsetParent.getBoundingClientRect()
+            var bodyPosition = ownerDocument.body.getBoundingClientRect()
+            offsetLeft = offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft
+            offsetTop = offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop
+        }
+        hints.style.left = left - offsetLeft + 'px'
+        hints.style.top = top - offsetTop + 'px'
+
+        // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+        var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth)
+        var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight)
+        container.appendChild(hints)
+        cm.getInputField().setAttribute('aria-autocomplete', 'list')
+        cm.getInputField().setAttribute('aria-owns', this.id)
+        cm.getInputField().setAttribute('aria-activedescendant', this.id + '-' + this.selectedHint)
+
+        var box = completion.options.moveOnOverlap ? hints.getBoundingClientRect() : new DOMRect()
+        var scrolls = completion.options.paddingForScrollbar ? hints.scrollHeight > hints.clientHeight + 1 : false
+
+        // Compute in the timeout to avoid reflow on init
+        var startScroll
+        setTimeout(function () {
+            startScroll = cm.getScrollInfo()
+        })
+
+        var overlapY = box.bottom - winH
+        if (overlapY > 0) {
+            var height = box.bottom - box.top,
+                curTop = pos.top - (pos.bottom - box.top)
+            if (curTop - height > 0) {
+                // Fits above cursor
+                hints.style.top = (top = pos.top - height - offsetTop) + 'px'
+                below = false
+            } else if (height > winH) {
+                hints.style.height = winH - 5 + 'px'
+                hints.style.top = (top = pos.bottom - box.top - offsetTop) + 'px'
+                var cursor = cm.getCursor()
+                if (data.from.ch != cursor.ch) {
+                    pos = cm.cursorCoords(cursor)
+                    hints.style.left = (left = pos.left - offsetLeft) + 'px'
+                    box = hints.getBoundingClientRect()
+                }
+            }
+        }
+        var overlapX = box.right - winW
+        if (scrolls) overlapX += cm.display.nativeBarWidth
+        if (overlapX > 0) {
+            if (box.right - box.left > winW) {
+                hints.style.width = winW - 5 + 'px'
+                overlapX -= box.right - box.left - winW
+            }
+            hints.style.left = (left = pos.left - overlapX - offsetLeft) + 'px'
+        }
+        if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling) node.style.paddingRight = cm.display.nativeBarWidth + 'px'
+
+        cm.addKeyMap(
+            (this.keyMap = buildKeyMap(completion, {
+                moveFocus: function (n, avoidWrap) {
+                    widget.changeActive(widget.selectedHint + n, avoidWrap)
+                },
+                setFocus: function (n) {
+                    widget.changeActive(n)
+                },
+                menuSize: function () {
+                    return widget.screenAmount()
+                },
+                length: completions.length,
+                close: function () {
+                    completion.close()
+                },
+                pick: function () {
+                    widget.pick()
+                },
+                data: data,
+            }))
+        )
+
+        if (completion.options.closeOnUnfocus) {
+            var closingOnBlur
+            cm.on(
+                'blur',
+                (this.onBlur = function () {
+                    closingOnBlur = setTimeout(function () {
+                        completion.close()
+                    }, 100)
+                })
+            )
+            cm.on(
+                'focus',
+                (this.onFocus = function () {
+                    clearTimeout(closingOnBlur)
+                })
+            )
+        }
+
+        cm.on(
+            'scroll',
+            (this.onScroll = function () {
+                var curScroll = cm.getScrollInfo(),
+                    editor = cm.getWrapperElement().getBoundingClientRect()
+                if (!startScroll) startScroll = cm.getScrollInfo()
+                var newTop = top + startScroll.top - curScroll.top
+                var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop)
+                if (!below) point += hints.offsetHeight
+                if (point <= editor.top || point >= editor.bottom) return completion.close()
+                hints.style.top = newTop + 'px'
+                hints.style.left = left + startScroll.left - curScroll.left + 'px'
+            })
+        )
+
+        CodeMirror.on(hints, 'dblclick', function (e) {
+            var t = getHintElement(hints, e.target || e.srcElement)
+            if (t && t.hintId != null) {
+                widget.changeActive(t.hintId)
+                widget.pick()
+            }
+        })
+
+        CodeMirror.on(hints, 'click', function (e) {
+            var t = getHintElement(hints, e.target || e.srcElement)
+            if (t && t.hintId != null) {
+                widget.changeActive(t.hintId)
+                if (completion.options.completeOnSingleClick) widget.pick()
+            }
+        })
+
+        CodeMirror.on(hints, 'mousedown', function () {
+            setTimeout(function () {
+                cm.focus()
+            }, 20)
+        })
+
+        // The first hint doesn't need to be scrolled to on init
+        var selectedHintRange = this.getSelectedHintRange()
+        if (selectedHintRange.from !== 0 || selectedHintRange.to !== 0) {
+            this.scrollToActive()
+        }
+
+        CodeMirror.signal(data, 'select', completions[this.selectedHint], hints.childNodes[this.selectedHint])
+        return true
+    }
+
+    Widget.prototype = {
+        close: function () {
+            if (this.completion.widget != this) return
+            this.completion.widget = null
+            if (this.hints.parentNode) this.hints.parentNode.removeChild(this.hints)
+            this.completion.cm.removeKeyMap(this.keyMap)
+            var input = this.completion.cm.getInputField()
+            input.removeAttribute('aria-activedescendant')
+            input.removeAttribute('aria-owns')
+
+            var cm = this.completion.cm
+            if (this.completion.options.closeOnUnfocus) {
+                cm.off('blur', this.onBlur)
+                cm.off('focus', this.onFocus)
+            }
+            cm.off('scroll', this.onScroll)
+        },
+
+        disable: function () {
+            this.completion.cm.removeKeyMap(this.keyMap)
+            var widget = this
+            this.keyMap = {
+                Enter: function () {
+                    widget.picked = true
+                },
+            }
+            this.completion.cm.addKeyMap(this.keyMap)
+        },
+
+        pick: function () {
+            this.completion.pick(this.data, this.selectedHint)
+        },
+
+        changeActive: function (i, avoidWrap) {
+            if (i >= this.data.list.length) i = avoidWrap ? this.data.list.length - 1 : 0
+            else if (i < 0) i = avoidWrap ? 0 : this.data.list.length - 1
+            if (this.selectedHint == i) return
+            var node = this.hints.childNodes[this.selectedHint]
+            if (node) {
+                node.className = node.className.replace(' ' + ACTIVE_HINT_ELEMENT_CLASS, '')
+                node.removeAttribute('aria-selected')
+            }
+            node = this.hints.childNodes[(this.selectedHint = i)]
+            node.className += ' ' + ACTIVE_HINT_ELEMENT_CLASS
+            node.setAttribute('aria-selected', 'true')
+            this.completion.cm.getInputField().setAttribute('aria-activedescendant', node.id)
+            this.scrollToActive()
+            CodeMirror.signal(this.data, 'select', this.data.list[this.selectedHint], node)
+        },
+
+        scrollToActive: function () {
+            var selectedHintRange = this.getSelectedHintRange()
+            var node1 = this.hints.childNodes[selectedHintRange.from]
+            var node2 = this.hints.childNodes[selectedHintRange.to]
+            var firstNode = this.hints.firstChild
+            if (node1.offsetTop < this.hints.scrollTop) this.hints.scrollTop = node1.offsetTop - firstNode.offsetTop
+            else if (node2.offsetTop + node2.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
+                this.hints.scrollTop = node2.offsetTop + node2.offsetHeight - this.hints.clientHeight + firstNode.offsetTop
+        },
+
+        screenAmount: function () {
+            return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1
+        },
+
+        getSelectedHintRange: function () {
+            var margin = this.completion.options.scrollMargin || 0
+            return {
+                from: Math.max(0, this.selectedHint - margin),
+                to: Math.min(this.data.list.length - 1, this.selectedHint + margin),
+            }
+        },
+    }
+
+    function applicableHelpers(cm, helpers) {
+        if (!cm.somethingSelected()) return helpers
+        var result = []
+        for (var i = 0; i < helpers.length; i++) if (helpers[i].supportsSelection) result.push(helpers[i])
+        return result
+    }
+
+    function fetchHints(hint, cm, options, callback) {
+        if (hint.async) {
+            hint(cm, callback, options)
+        } else {
+            var result = hint(cm, options)
+            if (result && result.then) result.then(callback)
+            else callback(result)
+        }
+    }
+
+    function resolveAutoHints(cm, pos) {
+        var helpers = cm.getHelpers(pos, 'hint'),
+            words
+        if (helpers.length) {
+            var resolved = function (cm, callback, options) {
+                var app = applicableHelpers(cm, helpers)
+                function run(i) {
+                    if (i == app.length) return callback(null)
+                    fetchHints(app[i], cm, options, function (result) {
+                        if (result && result.list.length > 0) callback(result)
+                        else run(i + 1)
+                    })
+                }
+                run(0)
+            }
+            resolved.async = true
+            resolved.supportsSelection = true
+            return resolved
+        } else if ((words = cm.getHelper(cm.getCursor(), 'hintWords'))) {
+            return function (cm) {
+                return CodeMirror.hint.fromList(cm, { words: words })
+            }
+        } else if (CodeMirror.hint.anyword) {
+            return function (cm, options) {
+                return CodeMirror.hint.anyword(cm, options)
+            }
+        } else {
+            return function () {}
+        }
+    }
+
+    CodeMirror.registerHelper('hint', 'auto', {
+        resolve: resolveAutoHints,
+    })
+
+    CodeMirror.registerHelper('hint', 'fromList', function (cm, options) {
+        var cur = cm.getCursor(),
+            token = cm.getTokenAt(cur)
+        var term,
+            from = CodeMirror.Pos(cur.line, token.start),
+            to = cur
+        if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
+            term = token.string.substr(0, cur.ch - token.start)
+        } else {
+            term = ''
+            from = cur
+        }
+        var found = []
+        for (var i = 0; i < options.words.length; i++) {
+            var word = options.words[i]
+            if (word.slice(0, term.length) == term) found.push(word)
+        }
+
+        if (found.length) return { list: found, from: from, to: to }
+    })
+
+    CodeMirror.commands.autocomplete = CodeMirror.showHint
+
+    var defaultOptions = {
+        hint: CodeMirror.hint.auto,
+        completeSingle: true,
+        alignWithWord: true,
+        closeCharacters: /[\s()\[\]{};:>,]/,
+        closeOnPick: true,
+        closeOnUnfocus: true,
+        updateOnCursorActivity: true,
+        completeOnSingleClick: true,
+        container: null,
+        customKeys: null,
+        extraKeys: null,
+        paddingForScrollbar: true,
+        moveOnOverlap: true,
+    }
+
+    CodeMirror.defineOption('hintOptions', null)
+})

+ 298 - 0
libs/codemirror/addon/hint/sql-hint.js

@@ -0,0 +1,298 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('../../mode/sql/sql'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', '../../mode/sql/sql'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var tables
+    var defaultTable
+    var keywords
+    var identifierQuote
+    var CONS = {
+        QUERY_DIV: ';',
+        ALIAS_KEYWORD: 'AS',
+    }
+    var Pos = CodeMirror.Pos,
+        cmpPos = CodeMirror.cmpPos
+
+    function isArray(val) {
+        return Object.prototype.toString.call(val) == '[object Array]'
+    }
+
+    function getKeywords(editor) {
+        var mode = editor.doc.modeOption
+        if (mode === 'sql') mode = 'text/x-sql'
+        return CodeMirror.resolveMode(mode).keywords
+    }
+
+    function getIdentifierQuote(editor) {
+        var mode = editor.doc.modeOption
+        if (mode === 'sql') mode = 'text/x-sql'
+        return CodeMirror.resolveMode(mode).identifierQuote || '`'
+    }
+
+    function getText(item) {
+        return typeof item == 'string' ? item : item.text
+    }
+
+    function wrapTable(name, value) {
+        if (isArray(value)) value = { columns: value }
+        if (!value.text) value.text = name
+        return value
+    }
+
+    function parseTables(input) {
+        var result = {}
+        if (isArray(input)) {
+            for (var i = input.length - 1; i >= 0; i--) {
+                var item = input[i]
+                result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
+            }
+        } else if (input) {
+            for (var name in input) result[name.toUpperCase()] = wrapTable(name, input[name])
+        }
+        return result
+    }
+
+    function getTable(name) {
+        return tables[name.toUpperCase()]
+    }
+
+    function shallowClone(object) {
+        var result = {}
+        for (var key in object) if (object.hasOwnProperty(key)) result[key] = object[key]
+        return result
+    }
+
+    function match(string, word) {
+        var len = string.length
+        var sub = getText(word).substr(0, len)
+        return string.toUpperCase() === sub.toUpperCase()
+    }
+
+    function addMatches(result, search, wordlist, formatter) {
+        if (isArray(wordlist)) {
+            for (var i = 0; i < wordlist.length; i++) if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
+        } else {
+            for (var word in wordlist)
+                if (wordlist.hasOwnProperty(word)) {
+                    var val = wordlist[word]
+                    if (!val || val === true) val = word
+                    else val = val.displayText ? { text: val.text, displayText: val.displayText } : val.text
+                    if (match(search, val)) result.push(formatter(val))
+                }
+        }
+    }
+
+    function cleanName(name) {
+        // Get rid name from identifierQuote and preceding dot(.)
+        if (name.charAt(0) == '.') {
+            name = name.substr(1)
+        }
+        // replace duplicated identifierQuotes with single identifierQuotes
+        // and remove single identifierQuotes
+        var nameParts = name.split(identifierQuote + identifierQuote)
+        for (var i = 0; i < nameParts.length; i++) nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote, 'g'), '')
+        return nameParts.join(identifierQuote)
+    }
+
+    function insertIdentifierQuotes(name) {
+        var nameParts = getText(name).split('.')
+        for (var i = 0; i < nameParts.length; i++)
+            nameParts[i] =
+                identifierQuote +
+                // duplicate identifierQuotes
+                nameParts[i].replace(new RegExp(identifierQuote, 'g'), identifierQuote + identifierQuote) +
+                identifierQuote
+        var escaped = nameParts.join('.')
+        if (typeof name == 'string') return escaped
+        name = shallowClone(name)
+        name.text = escaped
+        return name
+    }
+
+    function nameCompletion(cur, token, result, editor) {
+        // Try to complete table, column names and return start position of completion
+        var useIdentifierQuotes = false
+        var nameParts = []
+        var start = token.start
+        var cont = true
+        while (cont) {
+            cont = token.string.charAt(0) == '.'
+            useIdentifierQuotes = useIdentifierQuotes || token.string.charAt(0) == identifierQuote
+
+            start = token.start
+            nameParts.unshift(cleanName(token.string))
+
+            token = editor.getTokenAt(Pos(cur.line, token.start))
+            if (token.string == '.') {
+                cont = true
+                token = editor.getTokenAt(Pos(cur.line, token.start))
+            }
+        }
+
+        // Try to complete table names
+        var string = nameParts.join('.')
+        addMatches(result, string, tables, function (w) {
+            return useIdentifierQuotes ? insertIdentifierQuotes(w) : w
+        })
+
+        // Try to complete columns from defaultTable
+        addMatches(result, string, defaultTable, function (w) {
+            return useIdentifierQuotes ? insertIdentifierQuotes(w) : w
+        })
+
+        // Try to complete columns
+        string = nameParts.pop()
+        var table = nameParts.join('.')
+
+        var alias = false
+        var aliasTable = table
+        // Check if table is available. If not, find table by Alias
+        if (!getTable(table)) {
+            var oldTable = table
+            table = findTableByAlias(table, editor)
+            if (table !== oldTable) alias = true
+        }
+
+        var columns = getTable(table)
+        if (columns && columns.columns) columns = columns.columns
+
+        if (columns) {
+            addMatches(result, string, columns, function (w) {
+                var tableInsert = table
+                if (alias == true) tableInsert = aliasTable
+                if (typeof w == 'string') {
+                    w = tableInsert + '.' + w
+                } else {
+                    w = shallowClone(w)
+                    w.text = tableInsert + '.' + w.text
+                }
+                return useIdentifierQuotes ? insertIdentifierQuotes(w) : w
+            })
+        }
+
+        return start
+    }
+
+    function eachWord(lineText, f) {
+        var words = lineText.split(/\s+/)
+        for (var i = 0; i < words.length; i++) if (words[i]) f(words[i].replace(/[`,;]/g, ''))
+    }
+
+    function findTableByAlias(alias, editor) {
+        var doc = editor.doc
+        var fullQuery = doc.getValue()
+        var aliasUpperCase = alias.toUpperCase()
+        var previousWord = ''
+        var table = ''
+        var separator = []
+        var validRange = {
+            start: Pos(0, 0),
+            end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length),
+        }
+
+        //add separator
+        var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV)
+        while (indexOfSeparator != -1) {
+            separator.push(doc.posFromIndex(indexOfSeparator))
+            indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator + 1)
+        }
+        separator.unshift(Pos(0, 0))
+        separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length))
+
+        //find valid range
+        var prevItem = null
+        var current = editor.getCursor()
+        for (var i = 0; i < separator.length; i++) {
+            if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
+                validRange = { start: prevItem, end: separator[i] }
+                break
+            }
+            prevItem = separator[i]
+        }
+
+        if (validRange.start) {
+            var query = doc.getRange(validRange.start, validRange.end, false)
+
+            for (var i = 0; i < query.length; i++) {
+                var lineText = query[i]
+                eachWord(lineText, function (word) {
+                    var wordUpperCase = word.toUpperCase()
+                    if (wordUpperCase === aliasUpperCase && getTable(previousWord)) table = previousWord
+                    if (wordUpperCase !== CONS.ALIAS_KEYWORD) previousWord = word
+                })
+                if (table) break
+            }
+        }
+        return table
+    }
+
+    CodeMirror.registerHelper('hint', 'sql', function (editor, options) {
+        tables = parseTables(options && options.tables)
+        var defaultTableName = options && options.defaultTable
+        var disableKeywords = options && options.disableKeywords
+        defaultTable = defaultTableName && getTable(defaultTableName)
+        keywords = getKeywords(editor)
+        identifierQuote = getIdentifierQuote(editor)
+
+        if (defaultTableName && !defaultTable) defaultTable = findTableByAlias(defaultTableName, editor)
+
+        defaultTable = defaultTable || []
+
+        if (defaultTable.columns) defaultTable = defaultTable.columns
+
+        var cur = editor.getCursor()
+        var result = []
+        var token = editor.getTokenAt(cur),
+            start,
+            end,
+            search
+        if (token.end > cur.ch) {
+            token.end = cur.ch
+            token.string = token.string.slice(0, cur.ch - token.start)
+        }
+
+        if (token.string.match(/^[.`"'\w@][\w$#]*$/g)) {
+            search = token.string
+            start = token.start
+            end = token.end
+        } else {
+            start = end = cur.ch
+            search = ''
+        }
+        if (search.charAt(0) == '.' || search.charAt(0) == identifierQuote) {
+            start = nameCompletion(cur, token, result, editor)
+        } else {
+            var objectOrClass = function (w, className) {
+                if (typeof w === 'object') {
+                    w.className = className
+                } else {
+                    w = { text: w, className: className }
+                }
+                return w
+            }
+            addMatches(result, search, defaultTable, function (w) {
+                return objectOrClass(w, 'CodeMirror-hint-table CodeMirror-hint-default-table')
+            })
+            addMatches(result, search, tables, function (w) {
+                return objectOrClass(w, 'CodeMirror-hint-table')
+            })
+            if (!disableKeywords)
+                addMatches(result, search, keywords, function (w) {
+                    return objectOrClass(w.toUpperCase(), 'CodeMirror-hint-keyword')
+                })
+        }
+
+        return { list: result, from: Pos(cur.line, start), to: Pos(cur.line, end) }
+    })
+})

+ 135 - 0
libs/codemirror/addon/hint/xml-hint.js

@@ -0,0 +1,135 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var Pos = CodeMirror.Pos
+
+    function matches(hint, typed, matchInMiddle) {
+        if (matchInMiddle) return hint.indexOf(typed) >= 0
+        else return hint.lastIndexOf(typed, 0) == 0
+    }
+
+    function getHints(cm, options) {
+        var tags = options && options.schemaInfo
+        var quote = (options && options.quoteChar) || '"'
+        var matchInMiddle = options && options.matchInMiddle
+        if (!tags) return
+        var cur = cm.getCursor(),
+            token = cm.getTokenAt(cur)
+        if (token.end > cur.ch) {
+            token.end = cur.ch
+            token.string = token.string.slice(0, cur.ch - token.start)
+        }
+        var inner = CodeMirror.innerMode(cm.getMode(), token.state)
+        if (!inner.mode.xmlCurrentTag) return
+        var result = [],
+            replaceToken = false,
+            prefix
+        var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string)
+        var tagName = tag && /^\w/.test(token.string),
+            tagStart
+
+        if (tagName) {
+            var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start)
+            var tagType = /<\/$/.test(before) ? 'close' : /<$/.test(before) ? 'open' : null
+            if (tagType) tagStart = token.start - (tagType == 'close' ? 2 : 1)
+        } else if (tag && token.string == '<') {
+            tagType = 'open'
+        } else if (tag && token.string == '</') {
+            tagType = 'close'
+        }
+
+        var tagInfo = inner.mode.xmlCurrentTag(inner.state)
+        if ((!tag && !tagInfo) || tagType) {
+            if (tagName) prefix = token.string
+            replaceToken = tagType
+            var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : []
+            var inner = context.length && context[context.length - 1]
+            var curTag = inner && tags[inner]
+            var childList = inner ? curTag && curTag.children : tags['!top']
+            if (childList && tagType != 'close') {
+                for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle)) result.push('<' + childList[i])
+            } else if (tagType != 'close') {
+                for (var name in tags) if (tags.hasOwnProperty(name) && name != '!top' && name != '!attrs' && (!prefix || matches(name, prefix, matchInMiddle))) result.push('<' + name)
+            }
+            if (inner && (!prefix || (tagType == 'close' && matches(inner, prefix, matchInMiddle)))) result.push('</' + inner + '>')
+        } else {
+            // Attribute completion
+            var curTag = tagInfo && tags[tagInfo.name],
+                attrs = curTag && curTag.attrs
+            var globalAttrs = tags['!attrs']
+            if (!attrs && !globalAttrs) return
+            if (!attrs) {
+                attrs = globalAttrs
+            } else if (globalAttrs) {
+                // Combine tag-local and global attributes
+                var set = {}
+                for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm]
+                for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm]
+                attrs = set
+            }
+            if (token.type == 'string' || token.string == '=') {
+                // A value
+                var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)), Pos(cur.line, token.type == 'string' ? token.start : token.end))
+                var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/),
+                    atValues
+                if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return
+                if (typeof atValues == 'function') atValues = atValues.call(this, cm) // Functions can be used to supply values for autocomplete widget
+                if (token.type == 'string') {
+                    prefix = token.string
+                    var n = 0
+                    if (/['"]/.test(token.string.charAt(0))) {
+                        quote = token.string.charAt(0)
+                        prefix = token.string.slice(1)
+                        n++
+                    }
+                    var len = token.string.length
+                    if (/['"]/.test(token.string.charAt(len - 1))) {
+                        quote = token.string.charAt(len - 1)
+                        prefix = token.string.substr(n, len - 2)
+                    }
+                    if (n) {
+                        // an opening quote
+                        var line = cm.getLine(cur.line)
+                        if (line.length > token.end && line.charAt(token.end) == quote) token.end++ // include a closing quote
+                    }
+                    replaceToken = true
+                }
+                var returnHintsFromAtValues = function (atValues) {
+                    if (atValues) for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle)) result.push(quote + atValues[i] + quote)
+                    return returnHints()
+                }
+                if (atValues && atValues.then) return atValues.then(returnHintsFromAtValues)
+                return returnHintsFromAtValues(atValues)
+            } else {
+                // An attribute name
+                if (token.type == 'attribute') {
+                    prefix = token.string
+                    replaceToken = true
+                }
+                for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle))) result.push(attr)
+            }
+        }
+        function returnHints() {
+            return {
+                list: result,
+                from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
+                to: replaceToken ? Pos(cur.line, token.end) : cur,
+            }
+        }
+        return returnHints()
+    }
+
+    CodeMirror.registerHelper('hint', 'xml', getHints)
+})

+ 42 - 0
libs/codemirror/addon/lint/coffeescript-lint.js

@@ -0,0 +1,42 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js
+
+// declare global: coffeelint
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.registerHelper('lint', 'coffeescript', function (text) {
+        var found = []
+        if (!window.coffeelint) {
+            if (window.console) {
+                window.console.error('Error: window.coffeelint not defined, CodeMirror CoffeeScript linting cannot run.')
+            }
+            return found
+        }
+        var parseError = function (err) {
+            var loc = err.lineNumber
+            found.push({ from: CodeMirror.Pos(loc - 1, 0), to: CodeMirror.Pos(loc, 0), severity: err.level, message: err.message })
+        }
+        try {
+            var res = coffeelint.lint(text)
+            for (var i = 0; i < res.length; i++) {
+                parseError(res[i])
+            }
+        } catch (e) {
+            found.push({ from: CodeMirror.Pos(e.location.first_line, 0), to: CodeMirror.Pos(e.location.last_line, e.location.last_column), severity: 'error', message: e.message })
+        }
+        return found
+    })
+})

+ 46 - 0
libs/codemirror/addon/lint/css-lint.js

@@ -0,0 +1,46 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on csslint.js from https://github.com/stubbornella/csslint
+
+// declare global: CSSLint
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.registerHelper('lint', 'css', function (text, options) {
+        var found = []
+        if (!window.CSSLint) {
+            if (window.console) {
+                window.console.error('Error: window.CSSLint not defined, CodeMirror CSS linting cannot run.')
+            }
+            return found
+        }
+        var results = CSSLint.verify(text, options),
+            messages = results.messages,
+            message = null
+        for (var i = 0; i < messages.length; i++) {
+            message = messages[i]
+            var startLine = message.line - 1,
+                endLine = message.line - 1,
+                startCol = message.col - 1,
+                endCol = message.col
+            found.push({
+                from: CodeMirror.Pos(startLine, startCol),
+                to: CodeMirror.Pos(endLine, endCol),
+                message: message.message,
+                severity: message.type,
+            })
+        }
+        return found
+    })
+})

+ 64 - 0
libs/codemirror/addon/lint/html-lint.js

@@ -0,0 +1,64 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js
+
+// declare global: HTMLHint
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('htmlhint'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', 'htmlhint'], mod)
+    // Plain browser env
+    else mod(CodeMirror, window.HTMLHint)
+})(function (CodeMirror, HTMLHint) {
+    'use strict'
+
+    var defaultRules = {
+        'tagname-lowercase': true,
+        'attr-lowercase': true,
+        'attr-value-double-quotes': true,
+        'doctype-first': false,
+        'tag-pair': true,
+        'spec-char-escape': true,
+        'id-unique': true,
+        'src-not-empty': true,
+        'attr-no-duplication': true,
+    }
+
+    CodeMirror.registerHelper('lint', 'html', function (text, options) {
+        var found = []
+        if (HTMLHint && !HTMLHint.verify) {
+            if (typeof HTMLHint.default !== 'undefined') {
+                HTMLHint = HTMLHint.default
+            } else {
+                HTMLHint = HTMLHint.HTMLHint
+            }
+        }
+        if (!HTMLHint) HTMLHint = window.HTMLHint
+        if (!HTMLHint) {
+            if (window.console) {
+                window.console.error('Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run.')
+            }
+            return found
+        }
+        var messages = HTMLHint.verify(text, (options && options.rules) || defaultRules)
+        for (var i = 0; i < messages.length; i++) {
+            var message = messages[i]
+            var startLine = message.line - 1,
+                endLine = message.line - 1,
+                startCol = message.col - 1,
+                endCol = message.col
+            found.push({
+                from: CodeMirror.Pos(startLine, startCol),
+                to: CodeMirror.Pos(endLine, endCol),
+                message: message.message,
+                severity: message.type,
+            })
+        }
+        return found
+    })
+})

+ 70 - 0
libs/codemirror/addon/lint/javascript-lint.js

@@ -0,0 +1,70 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on jshint.js from https://github.com/jshint/jshint
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+    // declare global: JSHINT
+
+    function validator(text, options) {
+        if (!window.JSHINT) {
+            if (window.console) {
+                window.console.error('Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.')
+            }
+            return []
+        }
+        if (!options.indent)
+            // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
+            options.indent = 1 // JSHint default value is 4
+        JSHINT(text, options, options.globals)
+        var errors = JSHINT.data().errors,
+            result = []
+        if (errors) parseErrors(errors, result)
+        return result
+    }
+
+    CodeMirror.registerHelper('lint', 'javascript', validator)
+
+    function parseErrors(errors, output) {
+        for (var i = 0; i < errors.length; i++) {
+            var error = errors[i]
+            if (error) {
+                if (error.line <= 0) {
+                    if (window.console) {
+                        window.console.warn('Cannot display JSHint error (invalid line ' + error.line + ')', error)
+                    }
+                    continue
+                }
+
+                var start = error.character - 1,
+                    end = start + 1
+                if (error.evidence) {
+                    var index = error.evidence.substring(start).search(/.\b/)
+                    if (index > -1) {
+                        end += index
+                    }
+                }
+
+                // Convert to format expected by validation service
+                var hint = {
+                    message: error.reason,
+                    severity: error.code ? (error.code.startsWith('W') ? 'warning' : 'error') : 'error',
+                    from: CodeMirror.Pos(error.line - 1, start),
+                    to: CodeMirror.Pos(error.line - 1, end),
+                }
+
+                output.push(hint)
+            }
+        }
+    }
+})

+ 40 - 0
libs/codemirror/addon/lint/json-lint.js

@@ -0,0 +1,40 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on jsonlint.js from https://github.com/zaach/jsonlint
+
+// declare global: jsonlint
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.registerHelper('lint', 'json', function (text) {
+        var found = []
+        if (!window.jsonlint) {
+            if (window.console) {
+                window.console.error('Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.')
+            }
+            return found
+        }
+        // for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError
+        // is a subproperty
+        var jsonlint = window.jsonlint.parser || window.jsonlint
+        jsonlint.parseError = function (str, hash) {
+            var loc = hash.loc
+            found.push({ from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), message: str })
+        }
+        try {
+            jsonlint.parse(text)
+        } catch (e) {}
+        return found
+    })
+})

+ 79 - 0
libs/codemirror/addon/lint/lint.css

@@ -0,0 +1,79 @@
+/* The lint marker gutter */
+.CodeMirror-lint-markers {
+  width: 16px;
+}
+
+.CodeMirror-lint-tooltip {
+  background-color: #ffd;
+  border: 1px solid black;
+  border-radius: 4px 4px 4px 4px;
+  color: black;
+  font-family: monospace;
+  font-size: 10pt;
+  overflow: hidden;
+  padding: 2px 5px;
+  position: fixed;
+  white-space: pre;
+  white-space: pre-wrap;
+  z-index: 100;
+  max-width: 600px;
+  opacity: 0;
+  transition: opacity .4s;
+  -moz-transition: opacity .4s;
+  -webkit-transition: opacity .4s;
+  -o-transition: opacity .4s;
+  -ms-transition: opacity .4s;
+}
+
+.CodeMirror-lint-mark {
+  background-position: left bottom;
+  background-repeat: repeat-x;
+}
+
+.CodeMirror-lint-mark-warning {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-mark-error {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==");
+}
+
+.CodeMirror-lint-marker {
+  background-position: center center;
+  background-repeat: no-repeat;
+  cursor: pointer;
+  display: inline-block;
+  height: 16px;
+  width: 16px;
+  vertical-align: middle;
+  position: relative;
+}
+
+.CodeMirror-lint-message {
+  padding-left: 18px;
+  background-position: top left;
+  background-repeat: no-repeat;
+}
+
+.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-multiple {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
+  background-repeat: no-repeat;
+  background-position: right bottom;
+  width: 100%; height: 100%;
+}
+
+.CodeMirror-lint-line-error {
+  background-color: rgba(183, 76, 81, 0.08);
+}
+
+.CodeMirror-lint-line-warning {
+  background-color: rgba(255, 211, 0, 0.1);
+}

+ 321 - 0
libs/codemirror/addon/lint/lint.js

@@ -0,0 +1,321 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+    var GUTTER_ID = 'CodeMirror-lint-markers'
+    var LINT_LINE_ID = 'CodeMirror-lint-line-'
+
+    function showTooltip(cm, e, content) {
+        var tt = document.createElement('div')
+        tt.className = 'CodeMirror-lint-tooltip cm-s-' + cm.options.theme
+        tt.appendChild(content.cloneNode(true))
+        if (cm.state.lint.options.selfContain) cm.getWrapperElement().appendChild(tt)
+        else document.body.appendChild(tt)
+
+        function position(e) {
+            if (!tt.parentNode) return CodeMirror.off(document, 'mousemove', position)
+            tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + 'px'
+            tt.style.left = e.clientX + 5 + 'px'
+        }
+        CodeMirror.on(document, 'mousemove', position)
+        position(e)
+        if (tt.style.opacity != null) tt.style.opacity = 1
+        return tt
+    }
+    function rm(elt) {
+        if (elt.parentNode) elt.parentNode.removeChild(elt)
+    }
+    function hideTooltip(tt) {
+        if (!tt.parentNode) return
+        if (tt.style.opacity == null) rm(tt)
+        tt.style.opacity = 0
+        setTimeout(function () {
+            rm(tt)
+        }, 600)
+    }
+
+    function showTooltipFor(cm, e, content, node) {
+        var tooltip = showTooltip(cm, e, content)
+        function hide() {
+            CodeMirror.off(node, 'mouseout', hide)
+            if (tooltip) {
+                hideTooltip(tooltip)
+                tooltip = null
+            }
+        }
+        var poll = setInterval(function () {
+            if (tooltip)
+                for (var n = node; ; n = n.parentNode) {
+                    if (n && n.nodeType == 11) n = n.host
+                    if (n == document.body) return
+                    if (!n) {
+                        hide()
+                        break
+                    }
+                }
+            if (!tooltip) return clearInterval(poll)
+        }, 400)
+        CodeMirror.on(node, 'mouseout', hide)
+    }
+
+    function LintState(cm, conf, hasGutter) {
+        this.marked = []
+        if (conf instanceof Function) conf = { getAnnotations: conf }
+        if (!conf || conf === true) conf = {}
+        this.options = {}
+        this.linterOptions = conf.options || {}
+        for (var prop in defaults) this.options[prop] = defaults[prop]
+        for (var prop in conf) {
+            if (defaults.hasOwnProperty(prop)) {
+                if (conf[prop] != null) this.options[prop] = conf[prop]
+            } else if (!conf.options) {
+                this.linterOptions[prop] = conf[prop]
+            }
+        }
+        this.timeout = null
+        this.hasGutter = hasGutter
+        this.onMouseOver = function (e) {
+            onMouseOver(cm, e)
+        }
+        this.waitingFor = 0
+    }
+
+    var defaults = {
+        highlightLines: false,
+        tooltips: true,
+        delay: 500,
+        lintOnChange: true,
+        getAnnotations: null,
+        async: false,
+        selfContain: null,
+        formatAnnotation: null,
+        onUpdateLinting: null,
+    }
+
+    function clearMarks(cm) {
+        var state = cm.state.lint
+        if (state.hasGutter) cm.clearGutter(GUTTER_ID)
+        if (state.options.highlightLines) clearErrorLines(cm)
+        for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear()
+        state.marked.length = 0
+    }
+
+    function clearErrorLines(cm) {
+        cm.eachLine(function (line) {
+            var has = line.wrapClass && /\bCodeMirror-lint-line-\w+\b/.exec(line.wrapClass)
+            if (has) cm.removeLineClass(line, 'wrap', has[0])
+        })
+    }
+
+    function makeMarker(cm, labels, severity, multiple, tooltips) {
+        var marker = document.createElement('div'),
+            inner = marker
+        marker.className = 'CodeMirror-lint-marker CodeMirror-lint-marker-' + severity
+        if (multiple) {
+            inner = marker.appendChild(document.createElement('div'))
+            inner.className = 'CodeMirror-lint-marker CodeMirror-lint-marker-multiple'
+        }
+
+        if (tooltips != false)
+            CodeMirror.on(inner, 'mouseover', function (e) {
+                showTooltipFor(cm, e, labels, inner)
+            })
+
+        return marker
+    }
+
+    function getMaxSeverity(a, b) {
+        if (a == 'error') return a
+        else return b
+    }
+
+    function groupByLine(annotations) {
+        var lines = []
+        for (var i = 0; i < annotations.length; ++i) {
+            var ann = annotations[i],
+                line = ann.from.line
+            ;(lines[line] || (lines[line] = [])).push(ann)
+        }
+        return lines
+    }
+
+    function annotationTooltip(ann) {
+        var severity = ann.severity
+        if (!severity) severity = 'error'
+        var tip = document.createElement('div')
+        tip.className = 'CodeMirror-lint-message CodeMirror-lint-message-' + severity
+        if (typeof ann.messageHTML != 'undefined') {
+            tip.innerHTML = ann.messageHTML
+        } else {
+            tip.appendChild(document.createTextNode(ann.message))
+        }
+        return tip
+    }
+
+    function lintAsync(cm, getAnnotations) {
+        var state = cm.state.lint
+        var id = ++state.waitingFor
+        function abort() {
+            id = -1
+            cm.off('change', abort)
+        }
+        cm.on('change', abort)
+        getAnnotations(
+            cm.getValue(),
+            function (annotations, arg2) {
+                cm.off('change', abort)
+                if (state.waitingFor != id) return
+                if (arg2 && annotations instanceof CodeMirror) annotations = arg2
+                cm.operation(function () {
+                    updateLinting(cm, annotations)
+                })
+            },
+            state.linterOptions,
+            cm
+        )
+    }
+
+    function startLinting(cm) {
+        var state = cm.state.lint
+        if (!state) return
+        var options = state.options
+        /*
+         * Passing rules in `options` property prevents JSHint (and other linters) from complaining
+         * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.
+         */
+        var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), 'lint')
+        if (!getAnnotations) return
+        if (options.async || getAnnotations.async) {
+            lintAsync(cm, getAnnotations)
+        } else {
+            var annotations = getAnnotations(cm.getValue(), state.linterOptions, cm)
+            if (!annotations) return
+            if (annotations.then)
+                annotations.then(function (issues) {
+                    cm.operation(function () {
+                        updateLinting(cm, issues)
+                    })
+                })
+            else
+                cm.operation(function () {
+                    updateLinting(cm, annotations)
+                })
+        }
+    }
+
+    function updateLinting(cm, annotationsNotSorted) {
+        var state = cm.state.lint
+        if (!state) return
+        var options = state.options
+        clearMarks(cm)
+
+        var annotations = groupByLine(annotationsNotSorted)
+
+        for (var line = 0; line < annotations.length; ++line) {
+            var anns = annotations[line]
+            if (!anns) continue
+
+            // filter out duplicate messages
+            var message = []
+            anns = anns.filter(function (item) {
+                return message.indexOf(item.message) > -1 ? false : message.push(item.message)
+            })
+
+            var maxSeverity = null
+            var tipLabel = state.hasGutter && document.createDocumentFragment()
+
+            for (var i = 0; i < anns.length; ++i) {
+                var ann = anns[i]
+                var severity = ann.severity
+                if (!severity) severity = 'error'
+                maxSeverity = getMaxSeverity(maxSeverity, severity)
+
+                if (options.formatAnnotation) ann = options.formatAnnotation(ann)
+                if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann))
+
+                if (ann.to)
+                    state.marked.push(
+                        cm.markText(ann.from, ann.to, {
+                            className: 'CodeMirror-lint-mark CodeMirror-lint-mark-' + severity,
+                            __annotation: ann,
+                        })
+                    )
+            }
+            // use original annotations[line] to show multiple messages
+            if (state.hasGutter) cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, annotations[line].length > 1, options.tooltips))
+
+            if (options.highlightLines) cm.addLineClass(line, 'wrap', LINT_LINE_ID + maxSeverity)
+        }
+        if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm)
+    }
+
+    function onChange(cm) {
+        var state = cm.state.lint
+        if (!state) return
+        clearTimeout(state.timeout)
+        state.timeout = setTimeout(function () {
+            startLinting(cm)
+        }, state.options.delay)
+    }
+
+    function popupTooltips(cm, annotations, e) {
+        var target = e.target || e.srcElement
+        var tooltip = document.createDocumentFragment()
+        for (var i = 0; i < annotations.length; i++) {
+            var ann = annotations[i]
+            tooltip.appendChild(annotationTooltip(ann))
+        }
+        showTooltipFor(cm, e, tooltip, target)
+    }
+
+    function onMouseOver(cm, e) {
+        var target = e.target || e.srcElement
+        if (!/\bCodeMirror-lint-mark-/.test(target.className)) return
+        var box = target.getBoundingClientRect(),
+            x = (box.left + box.right) / 2,
+            y = (box.top + box.bottom) / 2
+        var spans = cm.findMarksAt(cm.coordsChar({ left: x, top: y }, 'client'))
+
+        var annotations = []
+        for (var i = 0; i < spans.length; ++i) {
+            var ann = spans[i].__annotation
+            if (ann) annotations.push(ann)
+        }
+        if (annotations.length) popupTooltips(cm, annotations, e)
+    }
+
+    CodeMirror.defineOption('lint', false, function (cm, val, old) {
+        if (old && old != CodeMirror.Init) {
+            clearMarks(cm)
+            if (cm.state.lint.options.lintOnChange !== false) cm.off('change', onChange)
+            CodeMirror.off(cm.getWrapperElement(), 'mouseover', cm.state.lint.onMouseOver)
+            clearTimeout(cm.state.lint.timeout)
+            delete cm.state.lint
+        }
+
+        if (val) {
+            var gutters = cm.getOption('gutters'),
+                hasLintGutter = false
+            for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true
+            var state = (cm.state.lint = new LintState(cm, val, hasLintGutter))
+            if (state.options.lintOnChange) cm.on('change', onChange)
+            if (state.options.tooltips != false && state.options.tooltips != 'gutter') CodeMirror.on(cm.getWrapperElement(), 'mouseover', state.onMouseOver)
+
+            startLinting(cm)
+        }
+    })
+
+    CodeMirror.defineExtension('performLint', function () {
+        startLinting(this)
+    })
+})

+ 43 - 0
libs/codemirror/addon/lint/yaml-lint.js

@@ -0,0 +1,43 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    // Depends on js-yaml.js from https://github.com/nodeca/js-yaml
+
+    // declare global: jsyaml
+
+    CodeMirror.registerHelper('lint', 'yaml', function (text) {
+        var found = []
+        if (!window.jsyaml) {
+            if (window.console) {
+                window.console.error('Error: window.jsyaml not defined, CodeMirror YAML linting cannot run.')
+            }
+            return found
+        }
+        try {
+            jsyaml.loadAll(text)
+        } catch (e) {
+            var loc = e.mark,
+                // js-yaml YAMLException doesn't always provide an accurate lineno
+                // e.g., when there are multiple yaml docs
+                // ---
+                // ---
+                // foo:bar
+                from = loc ? CodeMirror.Pos(loc.line, loc.column) : CodeMirror.Pos(0, 0),
+                to = from
+            found.push({ from: from, to: to, message: e.message })
+        }
+        return found
+    })
+})

+ 119 - 0
libs/codemirror/addon/merge/merge.css

@@ -0,0 +1,119 @@
+.CodeMirror-merge {
+  position: relative;
+  border: 1px solid #ddd;
+  white-space: pre;
+}
+
+.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
+  height: 350px;
+}
+
+.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }
+.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }
+.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
+.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
+
+.CodeMirror-merge-pane {
+  display: inline-block;
+  white-space: normal;
+  vertical-align: top;
+}
+.CodeMirror-merge-pane-rightmost {
+  position: absolute;
+  right: 0px;
+  z-index: 1;
+}
+
+.CodeMirror-merge-gap {
+  z-index: 2;
+  display: inline-block;
+  height: 100%;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  overflow: hidden;
+  border-left: 1px solid #ddd;
+  border-right: 1px solid #ddd;
+  position: relative;
+  background: #f8f8f8;
+}
+
+.CodeMirror-merge-scrolllock-wrap {
+  position: absolute;
+  bottom: 0; left: 50%;
+}
+.CodeMirror-merge-scrolllock {
+  position: relative;
+  left: -50%;
+  cursor: pointer;
+  color: #555;
+  line-height: 1;
+}
+.CodeMirror-merge-scrolllock:after {
+  content: "\21db\00a0\00a0\21da";
+}
+.CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after {
+  content: "\21db\21da";
+}
+
+.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
+  position: absolute;
+  left: 0; top: 0;
+  right: 0; bottom: 0;
+  line-height: 1;
+}
+
+.CodeMirror-merge-copy {
+  position: absolute;
+  cursor: pointer;
+  color: #44c;
+  z-index: 3;
+}
+
+.CodeMirror-merge-copy-reverse {
+  position: absolute;
+  cursor: pointer;
+  color: #44c;
+}
+
+.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
+.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
+
+.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
+  background-position: bottom left;
+  background-repeat: repeat-x;
+}
+
+.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
+  background-position: bottom left;
+  background-repeat: repeat-x;
+}
+
+.CodeMirror-merge-r-chunk { background: #ffffe0; }
+.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }
+.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }
+.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }
+
+.CodeMirror-merge-l-chunk { background: #eef; }
+.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
+.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
+.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
+
+.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
+.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
+.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
+
+.CodeMirror-merge-collapsed-widget:before {
+  content: "(...)";
+}
+.CodeMirror-merge-collapsed-widget {
+  cursor: pointer;
+  color: #88b;
+  background: #eef;
+  border: 1px solid #ddf;
+  font-size: 90%;
+  padding: 0 3px;
+  border-radius: 4px;
+}
+.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1082 - 0
libs/codemirror/addon/merge/merge.js


+ 81 - 0
libs/codemirror/addon/mode/loadmode.js

@@ -0,0 +1,81 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), 'cjs')
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], function (CM) {
+            mod(CM, 'amd')
+        })
+    // Plain browser env
+    else mod(CodeMirror, 'plain')
+})(function (CodeMirror, env) {
+    if (!CodeMirror.modeURL) CodeMirror.modeURL = '../mode/%N/%N.js'
+
+    var loading = {}
+    function splitCallback(cont, n) {
+        var countDown = n
+        return function () {
+            if (--countDown == 0) cont()
+        }
+    }
+    function ensureDeps(mode, cont, options) {
+        var modeObj = CodeMirror.modes[mode],
+            deps = modeObj && modeObj.dependencies
+        if (!deps) return cont()
+        var missing = []
+        for (var i = 0; i < deps.length; ++i) {
+            if (!CodeMirror.modes.hasOwnProperty(deps[i])) missing.push(deps[i])
+        }
+        if (!missing.length) return cont()
+        var split = splitCallback(cont, missing.length)
+        for (var i = 0; i < missing.length; ++i) CodeMirror.requireMode(missing[i], split, options)
+    }
+
+    CodeMirror.requireMode = function (mode, cont, options) {
+        if (typeof mode != 'string') mode = mode.name
+        if (CodeMirror.modes.hasOwnProperty(mode)) return ensureDeps(mode, cont, options)
+        if (loading.hasOwnProperty(mode)) return loading[mode].push(cont)
+
+        var file = options && options.path ? options.path(mode) : CodeMirror.modeURL.replace(/%N/g, mode)
+        if (options && options.loadMode) {
+            options.loadMode(file, function () {
+                ensureDeps(mode, cont, options)
+            })
+        } else if (env == 'plain') {
+            var script = document.createElement('script')
+            script.src = file
+            var others = document.getElementsByTagName('script')[0]
+            var list = (loading[mode] = [cont])
+            CodeMirror.on(script, 'load', function () {
+                ensureDeps(
+                    mode,
+                    function () {
+                        for (var i = 0; i < list.length; ++i) list[i]()
+                    },
+                    options
+                )
+            })
+            others.parentNode.insertBefore(script, others)
+        } else if (env == 'cjs') {
+            require(file)
+            cont()
+        } else if (env == 'amd') {
+            requirejs([file], cont)
+        }
+    }
+
+    CodeMirror.autoLoadMode = function (instance, mode, options) {
+        if (!CodeMirror.modes.hasOwnProperty(mode))
+            CodeMirror.requireMode(
+                mode,
+                function () {
+                    instance.setOption('mode', instance.getOption('mode'))
+                },
+                options
+            )
+    }
+})

+ 137 - 0
libs/codemirror/addon/mode/multiplex.js

@@ -0,0 +1,137 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.multiplexingMode = function (outer /*, others */) {
+        // Others should be {open, close, mode [, delimStyle] [, innerStyle] [, parseDelimiters]} objects
+        var others = Array.prototype.slice.call(arguments, 1)
+
+        function indexOf(string, pattern, from, returnEnd) {
+            if (typeof pattern == 'string') {
+                var found = string.indexOf(pattern, from)
+                return returnEnd && found > -1 ? found + pattern.length : found
+            }
+            var m = pattern.exec(from ? string.slice(from) : string)
+            return m ? m.index + from + (returnEnd ? m[0].length : 0) : -1
+        }
+
+        return {
+            startState: function () {
+                return {
+                    outer: CodeMirror.startState(outer),
+                    innerActive: null,
+                    inner: null,
+                    startingInner: false,
+                }
+            },
+
+            copyState: function (state) {
+                return {
+                    outer: CodeMirror.copyState(outer, state.outer),
+                    innerActive: state.innerActive,
+                    inner: state.innerActive && CodeMirror.copyState(state.innerActive.mode, state.inner),
+                    startingInner: state.startingInner,
+                }
+            },
+
+            token: function (stream, state) {
+                if (!state.innerActive) {
+                    var cutOff = Infinity,
+                        oldContent = stream.string
+                    for (var i = 0; i < others.length; ++i) {
+                        var other = others[i]
+                        var found = indexOf(oldContent, other.open, stream.pos)
+                        if (found == stream.pos) {
+                            if (!other.parseDelimiters) stream.match(other.open)
+                            state.startingInner = !!other.parseDelimiters
+                            state.innerActive = other
+
+                            // Get the outer indent, making sure to handle CodeMirror.Pass
+                            var outerIndent = 0
+                            if (outer.indent) {
+                                var possibleOuterIndent = outer.indent(state.outer, '', '')
+                                if (possibleOuterIndent !== CodeMirror.Pass) outerIndent = possibleOuterIndent
+                            }
+
+                            state.inner = CodeMirror.startState(other.mode, outerIndent)
+                            return other.delimStyle && other.delimStyle + ' ' + other.delimStyle + '-open'
+                        } else if (found != -1 && found < cutOff) {
+                            cutOff = found
+                        }
+                    }
+                    if (cutOff != Infinity) stream.string = oldContent.slice(0, cutOff)
+                    var outerToken = outer.token(stream, state.outer)
+                    if (cutOff != Infinity) stream.string = oldContent
+                    return outerToken
+                } else {
+                    var curInner = state.innerActive,
+                        oldContent = stream.string
+                    if (!curInner.close && stream.sol()) {
+                        state.innerActive = state.inner = null
+                        return this.token(stream, state)
+                    }
+                    var found = curInner.close && !state.startingInner ? indexOf(oldContent, curInner.close, stream.pos, curInner.parseDelimiters) : -1
+                    if (found == stream.pos && !curInner.parseDelimiters) {
+                        stream.match(curInner.close)
+                        state.innerActive = state.inner = null
+                        return curInner.delimStyle && curInner.delimStyle + ' ' + curInner.delimStyle + '-close'
+                    }
+                    if (found > -1) stream.string = oldContent.slice(0, found)
+                    var innerToken = curInner.mode.token(stream, state.inner)
+                    if (found > -1) stream.string = oldContent
+                    else if (stream.pos > stream.start) state.startingInner = false
+
+                    if (found == stream.pos && curInner.parseDelimiters) state.innerActive = state.inner = null
+
+                    if (curInner.innerStyle) {
+                        if (innerToken) innerToken = innerToken + ' ' + curInner.innerStyle
+                        else innerToken = curInner.innerStyle
+                    }
+
+                    return innerToken
+                }
+            },
+
+            indent: function (state, textAfter, line) {
+                var mode = state.innerActive ? state.innerActive.mode : outer
+                if (!mode.indent) return CodeMirror.Pass
+                return mode.indent(state.innerActive ? state.inner : state.outer, textAfter, line)
+            },
+
+            blankLine: function (state) {
+                var mode = state.innerActive ? state.innerActive.mode : outer
+                if (mode.blankLine) {
+                    mode.blankLine(state.innerActive ? state.inner : state.outer)
+                }
+                if (!state.innerActive) {
+                    for (var i = 0; i < others.length; ++i) {
+                        var other = others[i]
+                        if (other.open === '\n') {
+                            state.innerActive = other
+                            state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, '', '') : 0)
+                        }
+                    }
+                } else if (state.innerActive.close === '\n') {
+                    state.innerActive = state.inner = null
+                }
+            },
+
+            electricChars: outer.electricChars,
+
+            innerMode: function (state) {
+                return state.inner ? { state: state.inner, mode: state.innerActive.mode } : { state: state.outer, mode: outer }
+            },
+        }
+    }
+})

+ 41 - 0
libs/codemirror/addon/mode/multiplex_test.js

@@ -0,0 +1,41 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function () {
+    CodeMirror.defineMode('markdown_with_stex', function () {
+        var inner = CodeMirror.getMode({}, 'stex')
+        var outer = CodeMirror.getMode({}, 'markdown')
+
+        var innerOptions = {
+            open: '$',
+            close: '$',
+            mode: inner,
+            delimStyle: 'delim',
+            innerStyle: 'inner',
+        }
+
+        return CodeMirror.multiplexingMode(outer, innerOptions)
+    })
+
+    var mode = CodeMirror.getMode({}, 'markdown_with_stex')
+
+    function MT(name) {
+        test.mode(name, mode, Array.prototype.slice.call(arguments, 1), 'multiplexing')
+    }
+
+    MT('stexInsideMarkdown', '[strong **Equation:**] [delim&delim-open $][inner&tag \\pi][delim&delim-close $]')
+
+    CodeMirror.defineMode('identical_delim_multiplex', function () {
+        return CodeMirror.multiplexingMode(CodeMirror.getMode({ indentUnit: 2 }, 'javascript'), {
+            open: '#',
+            close: '#',
+            mode: CodeMirror.getMode({}, 'markdown'),
+            parseDelimiters: true,
+            innerStyle: 'q',
+        })
+    })
+
+    var mode2 = CodeMirror.getMode({}, 'identical_delim_multiplex')
+
+    test.mode('identical_delimiters_with_parseDelimiters', mode2, ['[keyword let] [def x] [operator =] [q #foo][q&em *bar*][q #];'], 'multiplexing')
+})()

+ 93 - 0
libs/codemirror/addon/mode/overlay.js

@@ -0,0 +1,93 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Utility function that allows modes to be combined. The mode given
+// as the base argument takes care of most of the normal mode
+// functionality, but a second (typically simple) mode is used, which
+// can override the style of text. Both modes get to parse all of the
+// text, but when both assign a non-null style to a piece of code, the
+// overlay wins, unless the combine argument was true and not overridden,
+// or state.overlay.combineTokens was true, in which case the styles are
+// combined.
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.overlayMode = function (base, overlay, combine) {
+        return {
+            startState: function () {
+                return {
+                    base: CodeMirror.startState(base),
+                    overlay: CodeMirror.startState(overlay),
+                    basePos: 0,
+                    baseCur: null,
+                    overlayPos: 0,
+                    overlayCur: null,
+                    streamSeen: null,
+                }
+            },
+            copyState: function (state) {
+                return {
+                    base: CodeMirror.copyState(base, state.base),
+                    overlay: CodeMirror.copyState(overlay, state.overlay),
+                    basePos: state.basePos,
+                    baseCur: null,
+                    overlayPos: state.overlayPos,
+                    overlayCur: null,
+                }
+            },
+
+            token: function (stream, state) {
+                if (stream != state.streamSeen || Math.min(state.basePos, state.overlayPos) < stream.start) {
+                    state.streamSeen = stream
+                    state.basePos = state.overlayPos = stream.start
+                }
+
+                if (stream.start == state.basePos) {
+                    state.baseCur = base.token(stream, state.base)
+                    state.basePos = stream.pos
+                }
+                if (stream.start == state.overlayPos) {
+                    stream.pos = stream.start
+                    state.overlayCur = overlay.token(stream, state.overlay)
+                    state.overlayPos = stream.pos
+                }
+                stream.pos = Math.min(state.basePos, state.overlayPos)
+
+                // state.overlay.combineTokens always takes precedence over combine,
+                // unless set to null
+                if (state.overlayCur == null) return state.baseCur
+                else if ((state.baseCur != null && state.overlay.combineTokens) || (combine && state.overlay.combineTokens == null)) return state.baseCur + ' ' + state.overlayCur
+                else return state.overlayCur
+            },
+
+            indent:
+                base.indent &&
+                function (state, textAfter, line) {
+                    return base.indent(state.base, textAfter, line)
+                },
+            electricChars: base.electricChars,
+
+            innerMode: function (state) {
+                return { state: state.base, mode: base }
+            },
+
+            blankLine: function (state) {
+                var baseToken, overlayToken
+                if (base.blankLine) baseToken = base.blankLine(state.base)
+                if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay)
+
+                return overlayToken == null ? baseToken : combine && baseToken != null ? baseToken + ' ' + overlayToken : overlayToken
+            },
+        }
+    }
+})

+ 212 - 0
libs/codemirror/addon/mode/simple.js

@@ -0,0 +1,212 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineSimpleMode = function (name, states) {
+        CodeMirror.defineMode(name, function (config) {
+            return CodeMirror.simpleMode(config, states)
+        })
+    }
+
+    CodeMirror.simpleMode = function (config, states) {
+        ensureState(states, 'start')
+        var states_ = {},
+            meta = states.meta || {},
+            hasIndentation = false
+        for (var state in states)
+            if (state != meta && states.hasOwnProperty(state)) {
+                var list = (states_[state] = []),
+                    orig = states[state]
+                for (var i = 0; i < orig.length; i++) {
+                    var data = orig[i]
+                    list.push(new Rule(data, states))
+                    if (data.indent || data.dedent) hasIndentation = true
+                }
+            }
+        var mode = {
+            startState: function () {
+                return { state: 'start', pending: null, local: null, localState: null, indent: hasIndentation ? [] : null }
+            },
+            copyState: function (state) {
+                var s = { state: state.state, pending: state.pending, local: state.local, localState: null, indent: state.indent && state.indent.slice(0) }
+                if (state.localState) s.localState = CodeMirror.copyState(state.local.mode, state.localState)
+                if (state.stack) s.stack = state.stack.slice(0)
+                for (var pers = state.persistentStates; pers; pers = pers.next)
+                    s.persistentStates = {
+                        mode: pers.mode,
+                        spec: pers.spec,
+                        state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),
+                        next: s.persistentStates,
+                    }
+                return s
+            },
+            token: tokenFunction(states_, config),
+            innerMode: function (state) {
+                return state.local && { mode: state.local.mode, state: state.localState }
+            },
+            indent: indentFunction(states_, meta),
+        }
+        if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop)) mode[prop] = meta[prop]
+        return mode
+    }
+
+    function ensureState(states, name) {
+        if (!states.hasOwnProperty(name)) throw new Error('Undefined state ' + name + ' in simple mode')
+    }
+
+    function toRegex(val, caret) {
+        if (!val) return /(?:)/
+        var flags = ''
+        if (val instanceof RegExp) {
+            if (val.ignoreCase) flags = 'i'
+            if (val.unicode) flags += 'u'
+            val = val.source
+        } else {
+            val = String(val)
+        }
+        return new RegExp((caret === false ? '' : '^') + '(?:' + val + ')', flags)
+    }
+
+    function asToken(val) {
+        if (!val) return null
+        if (val.apply) return val
+        if (typeof val == 'string') return val.replace(/\./g, ' ')
+        var result = []
+        for (var i = 0; i < val.length; i++) result.push(val[i] && val[i].replace(/\./g, ' '))
+        return result
+    }
+
+    function Rule(data, states) {
+        if (data.next || data.push) ensureState(states, data.next || data.push)
+        this.regex = toRegex(data.regex)
+        this.token = asToken(data.token)
+        this.data = data
+    }
+
+    function tokenFunction(states, config) {
+        return function (stream, state) {
+            if (state.pending) {
+                var pend = state.pending.shift()
+                if (state.pending.length == 0) state.pending = null
+                stream.pos += pend.text.length
+                return pend.token
+            }
+
+            if (state.local) {
+                if (state.local.end && stream.match(state.local.end)) {
+                    var tok = state.local.endToken || null
+                    state.local = state.localState = null
+                    return tok
+                } else {
+                    var tok = state.local.mode.token(stream, state.localState),
+                        m
+                    if (state.local.endScan && (m = state.local.endScan.exec(stream.current()))) stream.pos = stream.start + m.index
+                    return tok
+                }
+            }
+
+            var curState = states[state.state]
+            for (var i = 0; i < curState.length; i++) {
+                var rule = curState[i]
+                var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex)
+                if (matches) {
+                    if (rule.data.next) {
+                        state.state = rule.data.next
+                    } else if (rule.data.push) {
+                        ;(state.stack || (state.stack = [])).push(state.state)
+                        state.state = rule.data.push
+                    } else if (rule.data.pop && state.stack && state.stack.length) {
+                        state.state = state.stack.pop()
+                    }
+
+                    if (rule.data.mode) enterLocalMode(config, state, rule.data.mode, rule.token)
+                    if (rule.data.indent) state.indent.push(stream.indentation() + config.indentUnit)
+                    if (rule.data.dedent) state.indent.pop()
+                    var token = rule.token
+                    if (token && token.apply) token = token(matches)
+                    if (matches.length > 2 && rule.token && typeof rule.token != 'string') {
+                        for (var j = 2; j < matches.length; j++) if (matches[j]) (state.pending || (state.pending = [])).push({ text: matches[j], token: rule.token[j - 1] })
+                        stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0))
+                        return token[0]
+                    } else if (token && token.join) {
+                        return token[0]
+                    } else {
+                        return token
+                    }
+                }
+            }
+            stream.next()
+            return null
+        }
+    }
+
+    function cmp(a, b) {
+        if (a === b) return true
+        if (!a || typeof a != 'object' || !b || typeof b != 'object') return false
+        var props = 0
+        for (var prop in a)
+            if (a.hasOwnProperty(prop)) {
+                if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false
+                props++
+            }
+        for (var prop in b) if (b.hasOwnProperty(prop)) props--
+        return props == 0
+    }
+
+    function enterLocalMode(config, state, spec, token) {
+        var pers
+        if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next) if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p
+        var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec)
+        var lState = pers ? pers.state : CodeMirror.startState(mode)
+        if (spec.persistent && !pers) state.persistentStates = { mode: mode, spec: spec.spec, state: lState, next: state.persistentStates }
+
+        state.localState = lState
+        state.local = {
+            mode: mode,
+            end: spec.end && toRegex(spec.end),
+            endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),
+            endToken: token && token.join ? token[token.length - 1] : token,
+        }
+    }
+
+    function indexOf(val, arr) {
+        for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true
+    }
+
+    function indentFunction(states, meta) {
+        return function (state, textAfter, line) {
+            if (state.local && state.local.mode.indent) return state.local.mode.indent(state.localState, textAfter, line)
+            if (state.indent == null || state.local || (meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)) return CodeMirror.Pass
+
+            var pos = state.indent.length - 1,
+                rules = states[state.state]
+            scan: for (;;) {
+                for (var i = 0; i < rules.length; i++) {
+                    var rule = rules[i]
+                    if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
+                        var m = rule.regex.exec(textAfter)
+                        if (m && m[0]) {
+                            pos--
+                            if (rule.next || rule.push) rules = states[rule.next || rule.push]
+                            textAfter = textAfter.slice(m[0].length)
+                            continue scan
+                        }
+                    }
+                }
+                break
+            }
+            return pos < 0 ? 0 : state.indent[pos]
+        }
+    }
+})

+ 42 - 0
libs/codemirror/addon/runmode/colorize.js

@@ -0,0 +1,42 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('./runmode'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', './runmode'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/
+
+    function textContent(node, out) {
+        if (node.nodeType == 3) return out.push(node.nodeValue)
+        for (var ch = node.firstChild; ch; ch = ch.nextSibling) {
+            textContent(ch, out)
+            if (isBlock.test(node.nodeType)) out.push('\n')
+        }
+    }
+
+    CodeMirror.colorize = function (collection, defaultMode) {
+        if (!collection) collection = document.body.getElementsByTagName('pre')
+
+        for (var i = 0; i < collection.length; ++i) {
+            var node = collection[i]
+            var mode = node.getAttribute('data-lang') || defaultMode
+            if (!mode) continue
+
+            var text = []
+            textContent(node, text)
+            node.innerHTML = ''
+            CodeMirror.runMode(text.join(''), mode, node)
+
+            node.className += ' cm-s-default'
+        }
+    }
+})

+ 423 - 0
libs/codemirror/addon/runmode/runmode-standalone.js

@@ -0,0 +1,423 @@
+;(function () {
+    'use strict'
+
+    function copyObj(obj, target, overwrite) {
+        if (!target) {
+            target = {}
+        }
+        for (var prop in obj) {
+            if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) {
+                target[prop] = obj[prop]
+            }
+        }
+        return target
+    }
+
+    // Counts the column offset in a string, taking tabs into account.
+    // Used mostly to find indentation.
+    function countColumn(string, end, tabSize, startIndex, startValue) {
+        if (end == null) {
+            end = string.search(/[^\s\u00a0]/)
+            if (end == -1) {
+                end = string.length
+            }
+        }
+        for (var i = startIndex || 0, n = startValue || 0; ; ) {
+            var nextTab = string.indexOf('\t', i)
+            if (nextTab < 0 || nextTab >= end) {
+                return n + (end - i)
+            }
+            n += nextTab - i
+            n += tabSize - (n % tabSize)
+            i = nextTab + 1
+        }
+    }
+
+    function nothing() {}
+
+    function createObj(base, props) {
+        var inst
+        if (Object.create) {
+            inst = Object.create(base)
+        } else {
+            nothing.prototype = base
+            inst = new nothing()
+        }
+        if (props) {
+            copyObj(props, inst)
+        }
+        return inst
+    }
+
+    // STRING STREAM
+
+    // Fed to the mode parsers, provides helper functions to make
+    // parsers more succinct.
+
+    var StringStream = function (string, tabSize, lineOracle) {
+        this.pos = this.start = 0
+        this.string = string
+        this.tabSize = tabSize || 8
+        this.lastColumnPos = this.lastColumnValue = 0
+        this.lineStart = 0
+        this.lineOracle = lineOracle
+    }
+
+    StringStream.prototype.eol = function () {
+        return this.pos >= this.string.length
+    }
+    StringStream.prototype.sol = function () {
+        return this.pos == this.lineStart
+    }
+    StringStream.prototype.peek = function () {
+        return this.string.charAt(this.pos) || undefined
+    }
+    StringStream.prototype.next = function () {
+        if (this.pos < this.string.length) {
+            return this.string.charAt(this.pos++)
+        }
+    }
+    StringStream.prototype.eat = function (match) {
+        var ch = this.string.charAt(this.pos)
+        var ok
+        if (typeof match == 'string') {
+            ok = ch == match
+        } else {
+            ok = ch && (match.test ? match.test(ch) : match(ch))
+        }
+        if (ok) {
+            ++this.pos
+            return ch
+        }
+    }
+    StringStream.prototype.eatWhile = function (match) {
+        var start = this.pos
+        while (this.eat(match)) {}
+        return this.pos > start
+    }
+    StringStream.prototype.eatSpace = function () {
+        var start = this.pos
+        while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) {
+            ++this.pos
+        }
+        return this.pos > start
+    }
+    StringStream.prototype.skipToEnd = function () {
+        this.pos = this.string.length
+    }
+    StringStream.prototype.skipTo = function (ch) {
+        var found = this.string.indexOf(ch, this.pos)
+        if (found > -1) {
+            this.pos = found
+            return true
+        }
+    }
+    StringStream.prototype.backUp = function (n) {
+        this.pos -= n
+    }
+    StringStream.prototype.column = function () {
+        if (this.lastColumnPos < this.start) {
+            this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
+            this.lastColumnPos = this.start
+        }
+        return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+    }
+    StringStream.prototype.indentation = function () {
+        return countColumn(this.string, null, this.tabSize) - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+    }
+    StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
+        if (typeof pattern == 'string') {
+            var cased = function (str) {
+                return caseInsensitive ? str.toLowerCase() : str
+            }
+            var substr = this.string.substr(this.pos, pattern.length)
+            if (cased(substr) == cased(pattern)) {
+                if (consume !== false) {
+                    this.pos += pattern.length
+                }
+                return true
+            }
+        } else {
+            var match = this.string.slice(this.pos).match(pattern)
+            if (match && match.index > 0) {
+                return null
+            }
+            if (match && consume !== false) {
+                this.pos += match[0].length
+            }
+            return match
+        }
+    }
+    StringStream.prototype.current = function () {
+        return this.string.slice(this.start, this.pos)
+    }
+    StringStream.prototype.hideFirstChars = function (n, inner) {
+        this.lineStart += n
+        try {
+            return inner()
+        } finally {
+            this.lineStart -= n
+        }
+    }
+    StringStream.prototype.lookAhead = function (n) {
+        var oracle = this.lineOracle
+        return oracle && oracle.lookAhead(n)
+    }
+    StringStream.prototype.baseToken = function () {
+        var oracle = this.lineOracle
+        return oracle && oracle.baseToken(this.pos)
+    }
+
+    // Known modes, by name and by MIME
+    var modes = {},
+        mimeModes = {}
+
+    // Extra arguments are stored as the mode's dependencies, which is
+    // used by (legacy) mechanisms like loadmode.js to automatically
+    // load a mode. (Preferred mechanism is the require/define calls.)
+    function defineMode(name, mode) {
+        if (arguments.length > 2) {
+            mode.dependencies = Array.prototype.slice.call(arguments, 2)
+        }
+        modes[name] = mode
+    }
+
+    function defineMIME(mime, spec) {
+        mimeModes[mime] = spec
+    }
+
+    // Given a MIME type, a {name, ...options} config object, or a name
+    // string, return a mode config object.
+    function resolveMode(spec) {
+        if (typeof spec == 'string' && mimeModes.hasOwnProperty(spec)) {
+            spec = mimeModes[spec]
+        } else if (spec && typeof spec.name == 'string' && mimeModes.hasOwnProperty(spec.name)) {
+            var found = mimeModes[spec.name]
+            if (typeof found == 'string') {
+                found = { name: found }
+            }
+            spec = createObj(found, spec)
+            spec.name = found.name
+        } else if (typeof spec == 'string' && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
+            return resolveMode('application/xml')
+        } else if (typeof spec == 'string' && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
+            return resolveMode('application/json')
+        }
+        if (typeof spec == 'string') {
+            return { name: spec }
+        } else {
+            return spec || { name: 'null' }
+        }
+    }
+
+    // Given a mode spec (anything that resolveMode accepts), find and
+    // initialize an actual mode object.
+    function getMode(options, spec) {
+        spec = resolveMode(spec)
+        var mfactory = modes[spec.name]
+        if (!mfactory) {
+            return getMode(options, 'text/plain')
+        }
+        var modeObj = mfactory(options, spec)
+        if (modeExtensions.hasOwnProperty(spec.name)) {
+            var exts = modeExtensions[spec.name]
+            for (var prop in exts) {
+                if (!exts.hasOwnProperty(prop)) {
+                    continue
+                }
+                if (modeObj.hasOwnProperty(prop)) {
+                    modeObj['_' + prop] = modeObj[prop]
+                }
+                modeObj[prop] = exts[prop]
+            }
+        }
+        modeObj.name = spec.name
+        if (spec.helperType) {
+            modeObj.helperType = spec.helperType
+        }
+        if (spec.modeProps) {
+            for (var prop$1 in spec.modeProps) {
+                modeObj[prop$1] = spec.modeProps[prop$1]
+            }
+        }
+
+        return modeObj
+    }
+
+    // This can be used to attach properties to mode objects from
+    // outside the actual mode definition.
+    var modeExtensions = {}
+    function extendMode(mode, properties) {
+        var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
+        copyObj(properties, exts)
+    }
+
+    function copyState(mode, state) {
+        if (state === true) {
+            return state
+        }
+        if (mode.copyState) {
+            return mode.copyState(state)
+        }
+        var nstate = {}
+        for (var n in state) {
+            var val = state[n]
+            if (val instanceof Array) {
+                val = val.concat([])
+            }
+            nstate[n] = val
+        }
+        return nstate
+    }
+
+    // Given a mode and a state (for that mode), find the inner mode and
+    // state at the position that the state refers to.
+    function innerMode(mode, state) {
+        var info
+        while (mode.innerMode) {
+            info = mode.innerMode(state)
+            if (!info || info.mode == mode) {
+                break
+            }
+            state = info.state
+            mode = info.mode
+        }
+        return info || { mode: mode, state: state }
+    }
+
+    function startState(mode, a1, a2) {
+        return mode.startState ? mode.startState(a1, a2) : true
+    }
+
+    var modeMethods = {
+        __proto__: null,
+        modes: modes,
+        mimeModes: mimeModes,
+        defineMode: defineMode,
+        defineMIME: defineMIME,
+        resolveMode: resolveMode,
+        getMode: getMode,
+        modeExtensions: modeExtensions,
+        extendMode: extendMode,
+        copyState: copyState,
+        innerMode: innerMode,
+        startState: startState,
+    }
+
+    // declare global: globalThis, CodeMirror
+
+    // Create a minimal CodeMirror needed to use runMode, and assign to root.
+    var root = typeof globalThis !== 'undefined' ? globalThis : window
+    root.CodeMirror = {}
+
+    // Copy StringStream and mode methods into CodeMirror object.
+    CodeMirror.StringStream = StringStream
+    for (var exported in modeMethods) {
+        CodeMirror[exported] = modeMethods[exported]
+    }
+
+    // Minimal default mode.
+    CodeMirror.defineMode('null', function () {
+        return {
+            token: function (stream) {
+                return stream.skipToEnd()
+            },
+        }
+    })
+    CodeMirror.defineMIME('text/plain', 'null')
+
+    CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min
+    CodeMirror.splitLines = function (string) {
+        return string.split(/\r?\n|\r/)
+    }
+    CodeMirror.countColumn = countColumn
+
+    CodeMirror.defaults = { indentUnit: 2 }
+
+    // CodeMirror, copyright (c) by Marijn Haverbeke and others
+    // Distributed under an MIT license: https://codemirror.net/LICENSE
+    ;(function (mod) {
+        if (typeof exports == 'object' && typeof module == 'object') {
+            // CommonJS
+            mod(require('../../lib/codemirror'))
+        } else if (typeof define == 'function' && define.amd) {
+            // AMD
+            define(['../../lib/codemirror'], mod)
+        } // Plain browser env
+        else {
+            mod(CodeMirror)
+        }
+    })(function (CodeMirror) {
+        CodeMirror.runMode = function (string, modespec, callback, options) {
+            var mode = CodeMirror.getMode(CodeMirror.defaults, modespec)
+            var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize
+
+            // Create a tokenizing callback function if passed-in callback is a DOM element.
+            if (callback.appendChild) {
+                var ie = /MSIE \d/.test(navigator.userAgent)
+                var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9)
+                var node = callback,
+                    col = 0
+                node.innerHTML = ''
+                callback = function (text, style) {
+                    if (text == '\n') {
+                        // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
+                        // Emitting a carriage return makes everything ok.
+                        node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text))
+                        col = 0
+                        return
+                    }
+                    var content = ''
+                    // replace tabs
+                    for (var pos = 0; ; ) {
+                        var idx = text.indexOf('\t', pos)
+                        if (idx == -1) {
+                            content += text.slice(pos)
+                            col += text.length - pos
+                            break
+                        } else {
+                            col += idx - pos
+                            content += text.slice(pos, idx)
+                            var size = tabSize - (col % tabSize)
+                            col += size
+                            for (var i = 0; i < size; ++i) {
+                                content += ' '
+                            }
+                            pos = idx + 1
+                        }
+                    }
+                    // Create a node with token style and append it to the callback DOM element.
+                    if (style) {
+                        var sp = node.appendChild(document.createElement('span'))
+                        sp.className = 'cm-' + style.replace(/ +/g, ' cm-')
+                        sp.appendChild(document.createTextNode(content))
+                    } else {
+                        node.appendChild(document.createTextNode(content))
+                    }
+                }
+            }
+
+            var lines = CodeMirror.splitLines(string),
+                state = (options && options.state) || CodeMirror.startState(mode)
+            for (var i = 0, e = lines.length; i < e; ++i) {
+                if (i) {
+                    callback('\n')
+                }
+                var stream = new CodeMirror.StringStream(lines[i], null, {
+                    lookAhead: function (n) {
+                        return lines[i + n]
+                    },
+                    baseToken: function () {},
+                })
+                if (!stream.string && mode.blankLine) {
+                    mode.blankLine(state)
+                }
+                while (!stream.eol()) {
+                    var style = mode.token(stream, state)
+                    callback(stream.current(), style, i, stream.start, state, mode)
+                    stream.start = stream.pos
+                }
+            }
+        }
+    })
+})()

+ 81 - 0
libs/codemirror/addon/runmode/runmode.js

@@ -0,0 +1,81 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.runMode = function (string, modespec, callback, options) {
+        var mode = CodeMirror.getMode(CodeMirror.defaults, modespec)
+        var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize
+
+        // Create a tokenizing callback function if passed-in callback is a DOM element.
+        if (callback.appendChild) {
+            var ie = /MSIE \d/.test(navigator.userAgent)
+            var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9)
+            var node = callback,
+                col = 0
+            node.innerHTML = ''
+            callback = function (text, style) {
+                if (text == '\n') {
+                    // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
+                    // Emitting a carriage return makes everything ok.
+                    node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text))
+                    col = 0
+                    return
+                }
+                var content = ''
+                // replace tabs
+                for (var pos = 0; ; ) {
+                    var idx = text.indexOf('\t', pos)
+                    if (idx == -1) {
+                        content += text.slice(pos)
+                        col += text.length - pos
+                        break
+                    } else {
+                        col += idx - pos
+                        content += text.slice(pos, idx)
+                        var size = tabSize - (col % tabSize)
+                        col += size
+                        for (var i = 0; i < size; ++i) content += ' '
+                        pos = idx + 1
+                    }
+                }
+                // Create a node with token style and append it to the callback DOM element.
+                if (style) {
+                    var sp = node.appendChild(document.createElement('span'))
+                    sp.className = 'cm-' + style.replace(/ +/g, ' cm-')
+                    sp.appendChild(document.createTextNode(content))
+                } else {
+                    node.appendChild(document.createTextNode(content))
+                }
+            }
+        }
+
+        var lines = CodeMirror.splitLines(string),
+            state = (options && options.state) || CodeMirror.startState(mode)
+        for (var i = 0, e = lines.length; i < e; ++i) {
+            if (i) callback('\n')
+            var stream = new CodeMirror.StringStream(lines[i], null, {
+                lookAhead: function (n) {
+                    return lines[i + n]
+                },
+                baseToken: function () {},
+            })
+            if (!stream.string && mode.blankLine) mode.blankLine(state)
+            while (!stream.eol()) {
+                var style = mode.token(stream, state)
+                callback(stream.current(), style, i, stream.start, state, mode)
+                stream.start = stream.pos
+            }
+        }
+    }
+})

+ 419 - 0
libs/codemirror/addon/runmode/runmode.node.js

@@ -0,0 +1,419 @@
+'use strict'
+
+function copyObj(obj, target, overwrite) {
+    if (!target) {
+        target = {}
+    }
+    for (var prop in obj) {
+        if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) {
+            target[prop] = obj[prop]
+        }
+    }
+    return target
+}
+
+// Counts the column offset in a string, taking tabs into account.
+// Used mostly to find indentation.
+function countColumn(string, end, tabSize, startIndex, startValue) {
+    if (end == null) {
+        end = string.search(/[^\s\u00a0]/)
+        if (end == -1) {
+            end = string.length
+        }
+    }
+    for (var i = startIndex || 0, n = startValue || 0; ; ) {
+        var nextTab = string.indexOf('\t', i)
+        if (nextTab < 0 || nextTab >= end) {
+            return n + (end - i)
+        }
+        n += nextTab - i
+        n += tabSize - (n % tabSize)
+        i = nextTab + 1
+    }
+}
+
+function nothing() {}
+
+function createObj(base, props) {
+    var inst
+    if (Object.create) {
+        inst = Object.create(base)
+    } else {
+        nothing.prototype = base
+        inst = new nothing()
+    }
+    if (props) {
+        copyObj(props, inst)
+    }
+    return inst
+}
+
+// STRING STREAM
+
+// Fed to the mode parsers, provides helper functions to make
+// parsers more succinct.
+
+var StringStream = function (string, tabSize, lineOracle) {
+    this.pos = this.start = 0
+    this.string = string
+    this.tabSize = tabSize || 8
+    this.lastColumnPos = this.lastColumnValue = 0
+    this.lineStart = 0
+    this.lineOracle = lineOracle
+}
+
+StringStream.prototype.eol = function () {
+    return this.pos >= this.string.length
+}
+StringStream.prototype.sol = function () {
+    return this.pos == this.lineStart
+}
+StringStream.prototype.peek = function () {
+    return this.string.charAt(this.pos) || undefined
+}
+StringStream.prototype.next = function () {
+    if (this.pos < this.string.length) {
+        return this.string.charAt(this.pos++)
+    }
+}
+StringStream.prototype.eat = function (match) {
+    var ch = this.string.charAt(this.pos)
+    var ok
+    if (typeof match == 'string') {
+        ok = ch == match
+    } else {
+        ok = ch && (match.test ? match.test(ch) : match(ch))
+    }
+    if (ok) {
+        ++this.pos
+        return ch
+    }
+}
+StringStream.prototype.eatWhile = function (match) {
+    var start = this.pos
+    while (this.eat(match)) {}
+    return this.pos > start
+}
+StringStream.prototype.eatSpace = function () {
+    var start = this.pos
+    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) {
+        ++this.pos
+    }
+    return this.pos > start
+}
+StringStream.prototype.skipToEnd = function () {
+    this.pos = this.string.length
+}
+StringStream.prototype.skipTo = function (ch) {
+    var found = this.string.indexOf(ch, this.pos)
+    if (found > -1) {
+        this.pos = found
+        return true
+    }
+}
+StringStream.prototype.backUp = function (n) {
+    this.pos -= n
+}
+StringStream.prototype.column = function () {
+    if (this.lastColumnPos < this.start) {
+        this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue)
+        this.lastColumnPos = this.start
+    }
+    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+}
+StringStream.prototype.indentation = function () {
+    return countColumn(this.string, null, this.tabSize) - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+}
+StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
+    if (typeof pattern == 'string') {
+        var cased = function (str) {
+            return caseInsensitive ? str.toLowerCase() : str
+        }
+        var substr = this.string.substr(this.pos, pattern.length)
+        if (cased(substr) == cased(pattern)) {
+            if (consume !== false) {
+                this.pos += pattern.length
+            }
+            return true
+        }
+    } else {
+        var match = this.string.slice(this.pos).match(pattern)
+        if (match && match.index > 0) {
+            return null
+        }
+        if (match && consume !== false) {
+            this.pos += match[0].length
+        }
+        return match
+    }
+}
+StringStream.prototype.current = function () {
+    return this.string.slice(this.start, this.pos)
+}
+StringStream.prototype.hideFirstChars = function (n, inner) {
+    this.lineStart += n
+    try {
+        return inner()
+    } finally {
+        this.lineStart -= n
+    }
+}
+StringStream.prototype.lookAhead = function (n) {
+    var oracle = this.lineOracle
+    return oracle && oracle.lookAhead(n)
+}
+StringStream.prototype.baseToken = function () {
+    var oracle = this.lineOracle
+    return oracle && oracle.baseToken(this.pos)
+}
+
+// Known modes, by name and by MIME
+var modes = {},
+    mimeModes = {}
+
+// Extra arguments are stored as the mode's dependencies, which is
+// used by (legacy) mechanisms like loadmode.js to automatically
+// load a mode. (Preferred mechanism is the require/define calls.)
+function defineMode(name, mode) {
+    if (arguments.length > 2) {
+        mode.dependencies = Array.prototype.slice.call(arguments, 2)
+    }
+    modes[name] = mode
+}
+
+function defineMIME(mime, spec) {
+    mimeModes[mime] = spec
+}
+
+// Given a MIME type, a {name, ...options} config object, or a name
+// string, return a mode config object.
+function resolveMode(spec) {
+    if (typeof spec == 'string' && mimeModes.hasOwnProperty(spec)) {
+        spec = mimeModes[spec]
+    } else if (spec && typeof spec.name == 'string' && mimeModes.hasOwnProperty(spec.name)) {
+        var found = mimeModes[spec.name]
+        if (typeof found == 'string') {
+            found = { name: found }
+        }
+        spec = createObj(found, spec)
+        spec.name = found.name
+    } else if (typeof spec == 'string' && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
+        return resolveMode('application/xml')
+    } else if (typeof spec == 'string' && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
+        return resolveMode('application/json')
+    }
+    if (typeof spec == 'string') {
+        return { name: spec }
+    } else {
+        return spec || { name: 'null' }
+    }
+}
+
+// Given a mode spec (anything that resolveMode accepts), find and
+// initialize an actual mode object.
+function getMode(options, spec) {
+    spec = resolveMode(spec)
+    var mfactory = modes[spec.name]
+    if (!mfactory) {
+        return getMode(options, 'text/plain')
+    }
+    var modeObj = mfactory(options, spec)
+    if (modeExtensions.hasOwnProperty(spec.name)) {
+        var exts = modeExtensions[spec.name]
+        for (var prop in exts) {
+            if (!exts.hasOwnProperty(prop)) {
+                continue
+            }
+            if (modeObj.hasOwnProperty(prop)) {
+                modeObj['_' + prop] = modeObj[prop]
+            }
+            modeObj[prop] = exts[prop]
+        }
+    }
+    modeObj.name = spec.name
+    if (spec.helperType) {
+        modeObj.helperType = spec.helperType
+    }
+    if (spec.modeProps) {
+        for (var prop$1 in spec.modeProps) {
+            modeObj[prop$1] = spec.modeProps[prop$1]
+        }
+    }
+
+    return modeObj
+}
+
+// This can be used to attach properties to mode objects from
+// outside the actual mode definition.
+var modeExtensions = {}
+function extendMode(mode, properties) {
+    var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {})
+    copyObj(properties, exts)
+}
+
+function copyState(mode, state) {
+    if (state === true) {
+        return state
+    }
+    if (mode.copyState) {
+        return mode.copyState(state)
+    }
+    var nstate = {}
+    for (var n in state) {
+        var val = state[n]
+        if (val instanceof Array) {
+            val = val.concat([])
+        }
+        nstate[n] = val
+    }
+    return nstate
+}
+
+// Given a mode and a state (for that mode), find the inner mode and
+// state at the position that the state refers to.
+function innerMode(mode, state) {
+    var info
+    while (mode.innerMode) {
+        info = mode.innerMode(state)
+        if (!info || info.mode == mode) {
+            break
+        }
+        state = info.state
+        mode = info.mode
+    }
+    return info || { mode: mode, state: state }
+}
+
+function startState(mode, a1, a2) {
+    return mode.startState ? mode.startState(a1, a2) : true
+}
+
+var modeMethods = {
+    __proto__: null,
+    modes: modes,
+    mimeModes: mimeModes,
+    defineMode: defineMode,
+    defineMIME: defineMIME,
+    resolveMode: resolveMode,
+    getMode: getMode,
+    modeExtensions: modeExtensions,
+    extendMode: extendMode,
+    copyState: copyState,
+    innerMode: innerMode,
+    startState: startState,
+}
+
+// Copy StringStream and mode methods into exports (CodeMirror) object.
+exports.StringStream = StringStream
+exports.countColumn = countColumn
+for (var exported in modeMethods) {
+    exports[exported] = modeMethods[exported]
+}
+
+// Shim library CodeMirror with the minimal CodeMirror defined above.
+require.cache[require.resolve('../../lib/codemirror')] = require.cache[require.resolve('./runmode.node')]
+require.cache[require.resolve('../../addon/runmode/runmode')] = require.cache[require.resolve('./runmode.node')]
+
+// Minimal default mode.
+exports.defineMode('null', function () {
+    return {
+        token: function (stream) {
+            return stream.skipToEnd()
+        },
+    }
+})
+exports.defineMIME('text/plain', 'null')
+
+exports.registerHelper = exports.registerGlobalHelper = Math.min
+exports.splitLines = function (string) {
+    return string.split(/\r?\n|\r/)
+}
+
+exports.defaults = { indentUnit: 2 }
+
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object') {
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    } else if (typeof define == 'function' && define.amd) {
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    } // Plain browser env
+    else {
+        mod(CodeMirror)
+    }
+})(function (CodeMirror) {
+    CodeMirror.runMode = function (string, modespec, callback, options) {
+        var mode = CodeMirror.getMode(CodeMirror.defaults, modespec)
+        var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize
+
+        // Create a tokenizing callback function if passed-in callback is a DOM element.
+        if (callback.appendChild) {
+            var ie = /MSIE \d/.test(navigator.userAgent)
+            var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9)
+            var node = callback,
+                col = 0
+            node.innerHTML = ''
+            callback = function (text, style) {
+                if (text == '\n') {
+                    // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
+                    // Emitting a carriage return makes everything ok.
+                    node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text))
+                    col = 0
+                    return
+                }
+                var content = ''
+                // replace tabs
+                for (var pos = 0; ; ) {
+                    var idx = text.indexOf('\t', pos)
+                    if (idx == -1) {
+                        content += text.slice(pos)
+                        col += text.length - pos
+                        break
+                    } else {
+                        col += idx - pos
+                        content += text.slice(pos, idx)
+                        var size = tabSize - (col % tabSize)
+                        col += size
+                        for (var i = 0; i < size; ++i) {
+                            content += ' '
+                        }
+                        pos = idx + 1
+                    }
+                }
+                // Create a node with token style and append it to the callback DOM element.
+                if (style) {
+                    var sp = node.appendChild(document.createElement('span'))
+                    sp.className = 'cm-' + style.replace(/ +/g, ' cm-')
+                    sp.appendChild(document.createTextNode(content))
+                } else {
+                    node.appendChild(document.createTextNode(content))
+                }
+            }
+        }
+
+        var lines = CodeMirror.splitLines(string),
+            state = (options && options.state) || CodeMirror.startState(mode)
+        for (var i = 0, e = lines.length; i < e; ++i) {
+            if (i) {
+                callback('\n')
+            }
+            var stream = new CodeMirror.StringStream(lines[i], null, {
+                lookAhead: function (n) {
+                    return lines[i + n]
+                },
+                baseToken: function () {},
+            })
+            if (!stream.string && mode.blankLine) {
+                mode.blankLine(state)
+            }
+            while (!stream.eol()) {
+                var style = mode.token(stream, state)
+                callback(stream.current(), style, i, stream.start, state, mode)
+                stream.start = stream.pos
+            }
+        }
+    }
+})

+ 138 - 0
libs/codemirror/addon/scroll/annotatescrollbar.js

@@ -0,0 +1,138 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineExtension('annotateScrollbar', function (options) {
+        if (typeof options == 'string') options = { className: options }
+        return new Annotation(this, options)
+    })
+
+    CodeMirror.defineOption('scrollButtonHeight', 0)
+
+    function Annotation(cm, options) {
+        this.cm = cm
+        this.options = options
+        this.buttonHeight = options.scrollButtonHeight || cm.getOption('scrollButtonHeight')
+        this.annotations = []
+        this.doRedraw = this.doUpdate = null
+        this.div = cm.getWrapperElement().appendChild(document.createElement('div'))
+        this.div.style.cssText = 'position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none'
+        this.computeScale()
+
+        function scheduleRedraw(delay) {
+            clearTimeout(self.doRedraw)
+            self.doRedraw = setTimeout(function () {
+                self.redraw()
+            }, delay)
+        }
+
+        var self = this
+        cm.on(
+            'refresh',
+            (this.resizeHandler = function () {
+                clearTimeout(self.doUpdate)
+                self.doUpdate = setTimeout(function () {
+                    if (self.computeScale()) scheduleRedraw(20)
+                }, 100)
+            })
+        )
+        cm.on('markerAdded', this.resizeHandler)
+        cm.on('markerCleared', this.resizeHandler)
+        if (options.listenForChanges !== false)
+            cm.on(
+                'changes',
+                (this.changeHandler = function () {
+                    scheduleRedraw(250)
+                })
+            )
+    }
+
+    Annotation.prototype.computeScale = function () {
+        var cm = this.cm
+        var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) / cm.getScrollerElement().scrollHeight
+        if (hScale != this.hScale) {
+            this.hScale = hScale
+            return true
+        }
+    }
+
+    Annotation.prototype.update = function (annotations) {
+        this.annotations = annotations
+        this.redraw()
+    }
+
+    Annotation.prototype.redraw = function (compute) {
+        if (compute !== false) this.computeScale()
+        var cm = this.cm,
+            hScale = this.hScale
+
+        var frag = document.createDocumentFragment(),
+            anns = this.annotations
+
+        var wrapping = cm.getOption('lineWrapping')
+        var singleLineH = wrapping && cm.defaultTextHeight() * 1.5
+        var curLine = null,
+            curLineObj = null
+
+        function getY(pos, top) {
+            if (curLine != pos.line) {
+                curLine = pos.line
+                curLineObj = cm.getLineHandle(pos.line)
+                var visual = cm.getLineHandleVisualStart(curLineObj)
+                if (visual != curLineObj) {
+                    curLine = cm.getLineNumber(visual)
+                    curLineObj = visual
+                }
+            }
+            if ((curLineObj.widgets && curLineObj.widgets.length) || (wrapping && curLineObj.height > singleLineH)) return cm.charCoords(pos, 'local')[top ? 'top' : 'bottom']
+            var topY = cm.heightAtLine(curLineObj, 'local')
+            return topY + (top ? 0 : curLineObj.height)
+        }
+
+        var lastLine = cm.lastLine()
+        if (cm.display.barWidth)
+            for (var i = 0, nextTop; i < anns.length; i++) {
+                var ann = anns[i]
+                if (ann.to.line > lastLine) continue
+                var top = nextTop || getY(ann.from, true) * hScale
+                var bottom = getY(ann.to, false) * hScale
+                while (i < anns.length - 1) {
+                    if (anns[i + 1].to.line > lastLine) break
+                    nextTop = getY(anns[i + 1].from, true) * hScale
+                    if (nextTop > bottom + 0.9) break
+                    ann = anns[++i]
+                    bottom = getY(ann.to, false) * hScale
+                }
+                if (bottom == top) continue
+                var height = Math.max(bottom - top, 3)
+
+                var elt = frag.appendChild(document.createElement('div'))
+                elt.style.cssText = 'position: absolute; right: 0px; width: ' + Math.max(cm.display.barWidth - 1, 2) + 'px; top: ' + (top + this.buttonHeight) + 'px; height: ' + height + 'px'
+                elt.className = this.options.className
+                if (ann.id) {
+                    elt.setAttribute('annotation-id', ann.id)
+                }
+            }
+        this.div.textContent = ''
+        this.div.appendChild(frag)
+    }
+
+    Annotation.prototype.clear = function () {
+        this.cm.off('refresh', this.resizeHandler)
+        this.cm.off('markerAdded', this.resizeHandler)
+        this.cm.off('markerCleared', this.resizeHandler)
+        if (this.changeHandler) this.cm.off('changes', this.changeHandler)
+        this.div.parentNode.removeChild(this.div)
+    }
+})

+ 49 - 0
libs/codemirror/addon/scroll/scrollpastend.js

@@ -0,0 +1,49 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineOption('scrollPastEnd', false, function (cm, val, old) {
+        if (old && old != CodeMirror.Init) {
+            cm.off('change', onChange)
+            cm.off('refresh', updateBottomMargin)
+            cm.display.lineSpace.parentNode.style.paddingBottom = ''
+            cm.state.scrollPastEndPadding = null
+        }
+        if (val) {
+            cm.on('change', onChange)
+            cm.on('refresh', updateBottomMargin)
+            updateBottomMargin(cm)
+        }
+    })
+
+    function onChange(cm, change) {
+        if (CodeMirror.changeEnd(change).line == cm.lastLine()) updateBottomMargin(cm)
+    }
+
+    function updateBottomMargin(cm) {
+        var padding = ''
+        if (cm.lineCount() > 1) {
+            var totalH = cm.display.scroller.clientHeight - 30,
+                lastLineH = cm.getLineHandle(cm.lastLine()).height
+            padding = totalH - lastLineH + 'px'
+        }
+        if (cm.state.scrollPastEndPadding != padding) {
+            cm.state.scrollPastEndPadding = padding
+            cm.display.lineSpace.parentNode.style.paddingBottom = padding
+            cm.off('refresh', updateBottomMargin)
+            cm.setSize()
+            cm.on('refresh', updateBottomMargin)
+        }
+    }
+})

+ 66 - 0
libs/codemirror/addon/scroll/simplescrollbars.css

@@ -0,0 +1,66 @@
+.CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
+  position: absolute;
+  background: #ccc;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  border: 1px solid #bbb;
+  border-radius: 2px;
+}
+
+.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
+  position: absolute;
+  z-index: 6;
+  background: #eee;
+}
+
+.CodeMirror-simplescroll-horizontal {
+  bottom: 0; left: 0;
+  height: 8px;
+}
+.CodeMirror-simplescroll-horizontal div {
+  bottom: 0;
+  height: 100%;
+}
+
+.CodeMirror-simplescroll-vertical {
+  right: 0; top: 0;
+  width: 8px;
+}
+.CodeMirror-simplescroll-vertical div {
+  right: 0;
+  width: 100%;
+}
+
+
+.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
+  display: none;
+}
+
+.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
+  position: absolute;
+  background: #bcd;
+  border-radius: 3px;
+}
+
+.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
+  position: absolute;
+  z-index: 6;
+}
+
+.CodeMirror-overlayscroll-horizontal {
+  bottom: 0; left: 0;
+  height: 6px;
+}
+.CodeMirror-overlayscroll-horizontal div {
+  bottom: 0;
+  height: 100%;
+}
+
+.CodeMirror-overlayscroll-vertical {
+  right: 0; top: 0;
+  width: 6px;
+}
+.CodeMirror-overlayscroll-vertical div {
+  right: 0;
+  width: 100%;
+}

+ 150 - 0
libs/codemirror/addon/scroll/simplescrollbars.js

@@ -0,0 +1,150 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    function Bar(cls, orientation, scroll) {
+        this.orientation = orientation
+        this.scroll = scroll
+        this.screen = this.total = this.size = 1
+        this.pos = 0
+
+        this.node = document.createElement('div')
+        this.node.className = cls + '-' + orientation
+        this.inner = this.node.appendChild(document.createElement('div'))
+
+        var self = this
+        CodeMirror.on(this.inner, 'mousedown', function (e) {
+            if (e.which != 1) return
+            CodeMirror.e_preventDefault(e)
+            var axis = self.orientation == 'horizontal' ? 'pageX' : 'pageY'
+            var start = e[axis],
+                startpos = self.pos
+            function done() {
+                CodeMirror.off(document, 'mousemove', move)
+                CodeMirror.off(document, 'mouseup', done)
+            }
+            function move(e) {
+                if (e.which != 1) return done()
+                self.moveTo(startpos + (e[axis] - start) * (self.total / self.size))
+            }
+            CodeMirror.on(document, 'mousemove', move)
+            CodeMirror.on(document, 'mouseup', done)
+        })
+
+        CodeMirror.on(this.node, 'click', function (e) {
+            CodeMirror.e_preventDefault(e)
+            var innerBox = self.inner.getBoundingClientRect(),
+                where
+            if (self.orientation == 'horizontal') where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0
+            else where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0
+            self.moveTo(self.pos + where * self.screen)
+        })
+
+        function onWheel(e) {
+            var moved = CodeMirror.wheelEventPixels(e)[self.orientation == 'horizontal' ? 'x' : 'y']
+            var oldPos = self.pos
+            self.moveTo(self.pos + moved)
+            if (self.pos != oldPos) CodeMirror.e_preventDefault(e)
+        }
+        CodeMirror.on(this.node, 'mousewheel', onWheel)
+        CodeMirror.on(this.node, 'DOMMouseScroll', onWheel)
+    }
+
+    Bar.prototype.setPos = function (pos, force) {
+        if (pos < 0) pos = 0
+        if (pos > this.total - this.screen) pos = this.total - this.screen
+        if (!force && pos == this.pos) return false
+        this.pos = pos
+        this.inner.style[this.orientation == 'horizontal' ? 'left' : 'top'] = pos * (this.size / this.total) + 'px'
+        return true
+    }
+
+    Bar.prototype.moveTo = function (pos) {
+        if (this.setPos(pos)) this.scroll(pos, this.orientation)
+    }
+
+    var minButtonSize = 10
+
+    Bar.prototype.update = function (scrollSize, clientSize, barSize) {
+        var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize
+        if (sizeChanged) {
+            this.screen = clientSize
+            this.total = scrollSize
+            this.size = barSize
+        }
+
+        var buttonSize = this.screen * (this.size / this.total)
+        if (buttonSize < minButtonSize) {
+            this.size -= minButtonSize - buttonSize
+            buttonSize = minButtonSize
+        }
+        this.inner.style[this.orientation == 'horizontal' ? 'width' : 'height'] = buttonSize + 'px'
+        this.setPos(this.pos, sizeChanged)
+    }
+
+    function SimpleScrollbars(cls, place, scroll) {
+        this.addClass = cls
+        this.horiz = new Bar(cls, 'horizontal', scroll)
+        place(this.horiz.node)
+        this.vert = new Bar(cls, 'vertical', scroll)
+        place(this.vert.node)
+        this.width = null
+    }
+
+    SimpleScrollbars.prototype.update = function (measure) {
+        if (this.width == null) {
+            var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle
+            if (style) this.width = parseInt(style.height)
+        }
+        var width = this.width || 0
+
+        var needsH = measure.scrollWidth > measure.clientWidth + 1
+        var needsV = measure.scrollHeight > measure.clientHeight + 1
+        this.vert.node.style.display = needsV ? 'block' : 'none'
+        this.horiz.node.style.display = needsH ? 'block' : 'none'
+
+        if (needsV) {
+            this.vert.update(measure.scrollHeight, measure.clientHeight, measure.viewHeight - (needsH ? width : 0))
+            this.vert.node.style.bottom = needsH ? width + 'px' : '0'
+        }
+        if (needsH) {
+            this.horiz.update(measure.scrollWidth, measure.clientWidth, measure.viewWidth - (needsV ? width : 0) - measure.barLeft)
+            this.horiz.node.style.right = needsV ? width + 'px' : '0'
+            this.horiz.node.style.left = measure.barLeft + 'px'
+        }
+
+        return { right: needsV ? width : 0, bottom: needsH ? width : 0 }
+    }
+
+    SimpleScrollbars.prototype.setScrollTop = function (pos) {
+        this.vert.setPos(pos)
+    }
+
+    SimpleScrollbars.prototype.setScrollLeft = function (pos) {
+        this.horiz.setPos(pos)
+    }
+
+    SimpleScrollbars.prototype.clear = function () {
+        var parent = this.horiz.node.parentNode
+        parent.removeChild(this.horiz.node)
+        parent.removeChild(this.vert.node)
+    }
+
+    CodeMirror.scrollbarModel.simple = function (place, scroll) {
+        return new SimpleScrollbars('CodeMirror-simplescroll', place, scroll)
+    }
+    CodeMirror.scrollbarModel.overlay = function (place, scroll) {
+        return new SimpleScrollbars('CodeMirror-overlayscroll', place, scroll)
+    }
+})

+ 60 - 0
libs/codemirror/addon/search/jump-to-line.js

@@ -0,0 +1,60 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Defines jumpToLine command. Uses dialog.js if present.
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('../dialog/dialog'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', '../dialog/dialog'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    // default search panel location
+    CodeMirror.defineOption('search', { bottom: false })
+
+    function dialog(cm, text, shortText, deflt, f) {
+        if (cm.openDialog) cm.openDialog(text, f, { value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom })
+        else f(prompt(shortText, deflt))
+    }
+
+    function getJumpDialog(cm) {
+        return (
+            cm.phrase('Jump to line:') +
+            ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' +
+            cm.phrase('(Use line:column or scroll% syntax)') +
+            '</span>'
+        )
+    }
+
+    function interpretLine(cm, string) {
+        var num = Number(string)
+        if (/^[-+]/.test(string)) return cm.getCursor().line + num
+        else return num - 1
+    }
+
+    CodeMirror.commands.jumpToLine = function (cm) {
+        var cur = cm.getCursor()
+        dialog(cm, getJumpDialog(cm), cm.phrase('Jump to line:'), cur.line + 1 + ':' + cur.ch, function (posStr) {
+            if (!posStr) return
+
+            var match
+            if ((match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr))) {
+                cm.setCursor(interpretLine(cm, match[1]), Number(match[2]))
+            } else if ((match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr))) {
+                var line = Math.round((cm.lineCount() * Number(match[1])) / 100)
+                if (/^[-+]/.test(match[1])) line = cur.line + line + 1
+                cm.setCursor(line - 1, cur.ch)
+            } else if ((match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr))) {
+                cm.setCursor(interpretLine(cm, match[1]), cur.ch)
+            }
+        })
+    }
+
+    CodeMirror.keyMap['default']['Alt-G'] = 'jumpToLine'
+})

+ 170 - 0
libs/codemirror/addon/search/match-highlighter.js

@@ -0,0 +1,170 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Highlighting text that matches the selection
+//
+// Defines an option highlightSelectionMatches, which, when enabled,
+// will style strings that match the selection throughout the
+// document.
+//
+// The option can be set to true to simply enable it, or to a
+// {minChars, style, wordsOnly, showToken, delay} object to explicitly
+// configure it. minChars is the minimum amount of characters that should be
+// selected for the behavior to occur, and style is the token style to
+// apply to the matches. This will be prefixed by "cm-" to create an
+// actual CSS class name. If wordsOnly is enabled, the matches will be
+// highlighted only if the selected text is a word. showToken, when enabled,
+// will cause the current token to be highlighted when nothing is selected.
+// delay is used to specify how much time to wait, in milliseconds, before
+// highlighting the matches. If annotateScrollbar is enabled, the occurrences
+// will be highlighted on the scrollbar via the matchesonscrollbar addon.
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('./matchesonscrollbar'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', './matchesonscrollbar'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var defaults = {
+        style: 'matchhighlight',
+        minChars: 2,
+        delay: 100,
+        wordsOnly: false,
+        annotateScrollbar: false,
+        showToken: false,
+        trim: true,
+    }
+
+    function State(options) {
+        this.options = {}
+        for (var name in defaults) this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]
+        this.overlay = this.timeout = null
+        this.matchesonscroll = null
+        this.active = false
+    }
+
+    CodeMirror.defineOption('highlightSelectionMatches', false, function (cm, val, old) {
+        if (old && old != CodeMirror.Init) {
+            removeOverlay(cm)
+            clearTimeout(cm.state.matchHighlighter.timeout)
+            cm.state.matchHighlighter = null
+            cm.off('cursorActivity', cursorActivity)
+            cm.off('focus', onFocus)
+        }
+        if (val) {
+            var state = (cm.state.matchHighlighter = new State(val))
+            if (cm.hasFocus()) {
+                state.active = true
+                highlightMatches(cm)
+            } else {
+                cm.on('focus', onFocus)
+            }
+            cm.on('cursorActivity', cursorActivity)
+        }
+    })
+
+    function cursorActivity(cm) {
+        var state = cm.state.matchHighlighter
+        if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)
+    }
+
+    function onFocus(cm) {
+        var state = cm.state.matchHighlighter
+        if (!state.active) {
+            state.active = true
+            scheduleHighlight(cm, state)
+        }
+    }
+
+    function scheduleHighlight(cm, state) {
+        clearTimeout(state.timeout)
+        state.timeout = setTimeout(function () {
+            highlightMatches(cm)
+        }, state.options.delay)
+    }
+
+    function addOverlay(cm, query, hasBoundary, style) {
+        var state = cm.state.matchHighlighter
+        cm.addOverlay((state.overlay = makeOverlay(query, hasBoundary, style)))
+        if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
+            var searchFor = hasBoundary
+                ? new RegExp((/\w/.test(query.charAt(0)) ? '\\b' : '') + query.replace(/[\\\[.+*?(){|^$]/g, '\\$&') + (/\w/.test(query.charAt(query.length - 1)) ? '\\b' : ''))
+                : query
+            state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false, { className: 'CodeMirror-selection-highlight-scrollbar' })
+        }
+    }
+
+    function removeOverlay(cm) {
+        var state = cm.state.matchHighlighter
+        if (state.overlay) {
+            cm.removeOverlay(state.overlay)
+            state.overlay = null
+            if (state.matchesonscroll) {
+                state.matchesonscroll.clear()
+                state.matchesonscroll = null
+            }
+        }
+    }
+
+    function highlightMatches(cm) {
+        cm.operation(function () {
+            var state = cm.state.matchHighlighter
+            removeOverlay(cm)
+            if (!cm.somethingSelected() && state.options.showToken) {
+                var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken
+                var cur = cm.getCursor(),
+                    line = cm.getLine(cur.line),
+                    start = cur.ch,
+                    end = start
+                while (start && re.test(line.charAt(start - 1))) --start
+                while (end < line.length && re.test(line.charAt(end))) ++end
+                if (start < end) addOverlay(cm, line.slice(start, end), re, state.options.style)
+                return
+            }
+            var from = cm.getCursor('from'),
+                to = cm.getCursor('to')
+            if (from.line != to.line) return
+            if (state.options.wordsOnly && !isWord(cm, from, to)) return
+            var selection = cm.getRange(from, to)
+            if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, '')
+            if (selection.length >= state.options.minChars) addOverlay(cm, selection, false, state.options.style)
+        })
+    }
+
+    function isWord(cm, from, to) {
+        var str = cm.getRange(from, to)
+        if (str.match(/^\w+$/) !== null) {
+            if (from.ch > 0) {
+                var pos = { line: from.line, ch: from.ch - 1 }
+                var chr = cm.getRange(pos, from)
+                if (chr.match(/\W/) === null) return false
+            }
+            if (to.ch < cm.getLine(from.line).length) {
+                var pos = { line: to.line, ch: to.ch + 1 }
+                var chr = cm.getRange(to, pos)
+                if (chr.match(/\W/) === null) return false
+            }
+            return true
+        } else return false
+    }
+
+    function boundariesAround(stream, re) {
+        return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) && (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)))
+    }
+
+    function makeOverlay(query, hasBoundary, style) {
+        return {
+            token: function (stream) {
+                if (stream.match(query) && (!hasBoundary || boundariesAround(stream, hasBoundary))) return style
+                stream.next()
+                stream.skipTo(query.charAt(0)) || stream.skipToEnd()
+            },
+        }
+    }
+})

+ 8 - 0
libs/codemirror/addon/search/matchesonscrollbar.css

@@ -0,0 +1,8 @@
+.CodeMirror-search-match {
+  background: gold;
+  border-top: 1px solid orange;
+  border-bottom: 1px solid orange;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  opacity: .5;
+}

+ 107 - 0
libs/codemirror/addon/search/matchesonscrollbar.js

@@ -0,0 +1,107 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('./searchcursor'), require('../scroll/annotatescrollbar'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', './searchcursor', '../scroll/annotatescrollbar'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineExtension('showMatchesOnScrollbar', function (query, caseFold, options) {
+        if (typeof options == 'string') options = { className: options }
+        if (!options) options = {}
+        return new SearchAnnotation(this, query, caseFold, options)
+    })
+
+    function SearchAnnotation(cm, query, caseFold, options) {
+        this.cm = cm
+        this.options = options
+        var annotateOptions = { listenForChanges: false }
+        for (var prop in options) annotateOptions[prop] = options[prop]
+        if (!annotateOptions.className) annotateOptions.className = 'CodeMirror-search-match'
+        this.annotation = cm.annotateScrollbar(annotateOptions)
+        this.query = query
+        this.caseFold = caseFold
+        this.gap = { from: cm.firstLine(), to: cm.lastLine() + 1 }
+        this.matches = []
+        this.update = null
+
+        this.findMatches()
+        this.annotation.update(this.matches)
+
+        var self = this
+        cm.on(
+            'change',
+            (this.changeHandler = function (_cm, change) {
+                self.onChange(change)
+            })
+        )
+    }
+
+    var MAX_MATCHES = 1000
+
+    SearchAnnotation.prototype.findMatches = function () {
+        if (!this.gap) return
+        for (var i = 0; i < this.matches.length; i++) {
+            var match = this.matches[i]
+            if (match.from.line >= this.gap.to) break
+            if (match.to.line >= this.gap.from) this.matches.splice(i--, 1)
+        }
+        var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), { caseFold: this.caseFold, multiline: this.options.multiline })
+        var maxMatches = (this.options && this.options.maxMatches) || MAX_MATCHES
+        while (cursor.findNext()) {
+            var match = { from: cursor.from(), to: cursor.to() }
+            if (match.from.line >= this.gap.to) break
+            this.matches.splice(i++, 0, match)
+            if (this.matches.length > maxMatches) break
+        }
+        this.gap = null
+    }
+
+    function offsetLine(line, changeStart, sizeChange) {
+        if (line <= changeStart) return line
+        return Math.max(changeStart, line + sizeChange)
+    }
+
+    SearchAnnotation.prototype.onChange = function (change) {
+        var startLine = change.from.line
+        var endLine = CodeMirror.changeEnd(change).line
+        var sizeChange = endLine - change.to.line
+        if (this.gap) {
+            this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line)
+            this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line)
+        } else {
+            this.gap = { from: change.from.line, to: endLine + 1 }
+        }
+
+        if (sizeChange)
+            for (var i = 0; i < this.matches.length; i++) {
+                var match = this.matches[i]
+                var newFrom = offsetLine(match.from.line, startLine, sizeChange)
+                if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch)
+                var newTo = offsetLine(match.to.line, startLine, sizeChange)
+                if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch)
+            }
+        clearTimeout(this.update)
+        var self = this
+        this.update = setTimeout(function () {
+            self.updateAfterChange()
+        }, 250)
+    }
+
+    SearchAnnotation.prototype.updateAfterChange = function () {
+        this.findMatches()
+        this.annotation.update(this.matches)
+    }
+
+    SearchAnnotation.prototype.clear = function () {
+        this.cm.off('change', this.changeHandler)
+        this.annotation.clear()
+    }
+})

+ 351 - 0
libs/codemirror/addon/search/search.js

@@ -0,0 +1,351 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Define search commands. Depends on dialog.js or another
+// implementation of the openDialog method.
+
+// Replace works a little oddly -- it will do the replace on the next
+// Ctrl-G (or whatever is bound to findNext) press. You prevent a
+// replace by making sure the match is no longer selected when hitting
+// Ctrl-G.
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'), require('./searchcursor'), require('../dialog/dialog'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror', './searchcursor', '../dialog/dialog'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    // default search panel location
+    CodeMirror.defineOption('search', { bottom: false })
+
+    function searchOverlay(query, caseInsensitive) {
+        if (typeof query == 'string') query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), caseInsensitive ? 'gi' : 'g')
+        else if (!query.global) query = new RegExp(query.source, query.ignoreCase ? 'gi' : 'g')
+
+        return {
+            token: function (stream) {
+                query.lastIndex = stream.pos
+                var match = query.exec(stream.string)
+                if (match && match.index == stream.pos) {
+                    stream.pos += match[0].length || 1
+                    return 'searching'
+                } else if (match) {
+                    stream.pos = match.index
+                } else {
+                    stream.skipToEnd()
+                }
+            },
+        }
+    }
+
+    function SearchState() {
+        this.posFrom = this.posTo = this.lastQuery = this.query = null
+        this.overlay = null
+    }
+
+    function getSearchState(cm) {
+        return cm.state.search || (cm.state.search = new SearchState())
+    }
+
+    function queryCaseInsensitive(query) {
+        return typeof query == 'string' && query == query.toLowerCase()
+    }
+
+    function getSearchCursor(cm, query, pos) {
+        // Heuristic: if the query string is all lowercase, do a case insensitive search.
+        return cm.getSearchCursor(query, pos, { caseFold: queryCaseInsensitive(query), multiline: true })
+    }
+
+    function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
+        cm.openDialog(text, onEnter, {
+            value: deflt,
+            selectValueOnOpen: true,
+            closeOnEnter: false,
+            onClose: function () {
+                clearSearch(cm)
+            },
+            onKeyDown: onKeyDown,
+            bottom: cm.options.search.bottom,
+        })
+    }
+
+    function dialog(cm, text, shortText, deflt, f) {
+        if (cm.openDialog) cm.openDialog(text, f, { value: deflt, selectValueOnOpen: true, bottom: cm.options.search.bottom })
+        else f(prompt(shortText, deflt))
+    }
+
+    function confirmDialog(cm, text, shortText, fs) {
+        if (cm.openConfirm) cm.openConfirm(text, fs)
+        else if (confirm(shortText)) fs[0]()
+    }
+
+    function parseString(string) {
+        return string.replace(/\\([nrt\\])/g, function (match, ch) {
+            if (ch == 'n') return '\n'
+            if (ch == 'r') return '\r'
+            if (ch == 't') return '\t'
+            if (ch == '\\') return '\\'
+            return match
+        })
+    }
+
+    function parseQuery(query) {
+        var isRE = query.match(/^\/(.*)\/([a-z]*)$/)
+        if (isRE) {
+            try {
+                query = new RegExp(isRE[1], isRE[2].indexOf('i') == -1 ? '' : 'i')
+            } catch (e) {} // Not a regular expression after all, do a string search
+        } else {
+            query = parseString(query)
+        }
+        if (typeof query == 'string' ? query == '' : query.test('')) query = /x^/
+        return query
+    }
+
+    function startSearch(cm, state, query) {
+        state.queryText = query
+        state.query = parseQuery(query)
+        cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query))
+        state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query))
+        cm.addOverlay(state.overlay)
+        if (cm.showMatchesOnScrollbar) {
+            if (state.annotate) {
+                state.annotate.clear()
+                state.annotate = null
+            }
+            state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query))
+        }
+    }
+
+    function doSearch(cm, rev, persistent, immediate) {
+        var state = getSearchState(cm)
+        if (state.query) return findNext(cm, rev)
+        var q = cm.getSelection() || state.lastQuery
+        if (q instanceof RegExp && q.source == 'x^') q = null
+        if (persistent && cm.openDialog) {
+            var hiding = null
+            var searchNext = function (query, event) {
+                CodeMirror.e_stop(event)
+                if (!query) return
+                if (query != state.queryText) {
+                    startSearch(cm, state, query)
+                    state.posFrom = state.posTo = cm.getCursor()
+                }
+                if (hiding) hiding.style.opacity = 1
+                findNext(cm, event.shiftKey, function (_, to) {
+                    var dialog
+                    if (
+                        to.line < 3 &&
+                        document.querySelector &&
+                        (dialog = cm.display.wrapper.querySelector('.CodeMirror-dialog')) &&
+                        dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, 'window').top
+                    )
+                        (hiding = dialog).style.opacity = 0.4
+                })
+            }
+            persistentDialog(cm, getQueryDialog(cm), q, searchNext, function (event, query) {
+                var keyName = CodeMirror.keyName(event)
+                var extra = cm.getOption('extraKeys'),
+                    cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption('keyMap')][keyName]
+                if (cmd == 'findNext' || cmd == 'findPrev' || cmd == 'findPersistentNext' || cmd == 'findPersistentPrev') {
+                    CodeMirror.e_stop(event)
+                    startSearch(cm, getSearchState(cm), query)
+                    cm.execCommand(cmd)
+                } else if (cmd == 'find' || cmd == 'findPersistent') {
+                    CodeMirror.e_stop(event)
+                    searchNext(query, event)
+                }
+            })
+            if (immediate && q) {
+                startSearch(cm, state, q)
+                findNext(cm, rev)
+            }
+        } else {
+            dialog(cm, getQueryDialog(cm), 'Search for:', q, function (query) {
+                if (query && !state.query)
+                    cm.operation(function () {
+                        startSearch(cm, state, query)
+                        state.posFrom = state.posTo = cm.getCursor()
+                        findNext(cm, rev)
+                    })
+            })
+        }
+    }
+
+    function findNext(cm, rev, callback) {
+        cm.operation(function () {
+            var state = getSearchState(cm)
+            var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo)
+            if (!cursor.find(rev)) {
+                cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0))
+                if (!cursor.find(rev)) return
+            }
+            cm.setSelection(cursor.from(), cursor.to())
+            cm.scrollIntoView({ from: cursor.from(), to: cursor.to() }, 20)
+            state.posFrom = cursor.from()
+            state.posTo = cursor.to()
+            if (callback) callback(cursor.from(), cursor.to())
+        })
+    }
+
+    function clearSearch(cm) {
+        cm.operation(function () {
+            var state = getSearchState(cm)
+            state.lastQuery = state.query
+            if (!state.query) return
+            state.query = state.queryText = null
+            cm.removeOverlay(state.overlay)
+            if (state.annotate) {
+                state.annotate.clear()
+                state.annotate = null
+            }
+        })
+    }
+
+    function el(tag, attrs) {
+        var element = tag ? document.createElement(tag) : document.createDocumentFragment()
+        for (var key in attrs) {
+            element[key] = attrs[key]
+        }
+        for (var i = 2; i < arguments.length; i++) {
+            var child = arguments[i]
+            element.appendChild(typeof child == 'string' ? document.createTextNode(child) : child)
+        }
+        return element
+    }
+
+    function getQueryDialog(cm) {
+        return el(
+            '',
+            null,
+            el('span', { className: 'CodeMirror-search-label' }, cm.phrase('Search:')),
+            ' ',
+            el('input', { type: 'text', style: 'width: 10em', className: 'CodeMirror-search-field' }),
+            ' ',
+            el('span', { style: 'color: #888', className: 'CodeMirror-search-hint' }, cm.phrase('(Use /re/ syntax for regexp search)'))
+        )
+    }
+    function getReplaceQueryDialog(cm) {
+        return el(
+            '',
+            null,
+            ' ',
+            el('input', { type: 'text', style: 'width: 10em', className: 'CodeMirror-search-field' }),
+            ' ',
+            el('span', { style: 'color: #888', className: 'CodeMirror-search-hint' }, cm.phrase('(Use /re/ syntax for regexp search)'))
+        )
+    }
+    function getReplacementQueryDialog(cm) {
+        return el('', null, el('span', { className: 'CodeMirror-search-label' }, cm.phrase('With:')), ' ', el('input', { type: 'text', style: 'width: 10em', className: 'CodeMirror-search-field' }))
+    }
+    function getDoReplaceConfirm(cm) {
+        return el(
+            '',
+            null,
+            el('span', { className: 'CodeMirror-search-label' }, cm.phrase('Replace?')),
+            ' ',
+            el('button', {}, cm.phrase('Yes')),
+            ' ',
+            el('button', {}, cm.phrase('No')),
+            ' ',
+            el('button', {}, cm.phrase('All')),
+            ' ',
+            el('button', {}, cm.phrase('Stop'))
+        )
+    }
+
+    function replaceAll(cm, query, text) {
+        cm.operation(function () {
+            for (var cursor = getSearchCursor(cm, query); cursor.findNext(); ) {
+                if (typeof query != 'string') {
+                    var match = cm.getRange(cursor.from(), cursor.to()).match(query)
+                    cursor.replace(
+                        text.replace(/\$(\d)/g, function (_, i) {
+                            return match[i]
+                        })
+                    )
+                } else cursor.replace(text)
+            }
+        })
+    }
+
+    function replace(cm, all) {
+        if (cm.getOption('readOnly')) return
+        var query = cm.getSelection() || getSearchState(cm).lastQuery
+        var dialogText = all ? cm.phrase('Replace all:') : cm.phrase('Replace:')
+        var fragment = el('', null, el('span', { className: 'CodeMirror-search-label' }, dialogText), getReplaceQueryDialog(cm))
+        dialog(cm, fragment, dialogText, query, function (query) {
+            if (!query) return
+            query = parseQuery(query)
+            dialog(cm, getReplacementQueryDialog(cm), cm.phrase('Replace with:'), '', function (text) {
+                text = parseString(text)
+                if (all) {
+                    replaceAll(cm, query, text)
+                } else {
+                    clearSearch(cm)
+                    var cursor = getSearchCursor(cm, query, cm.getCursor('from'))
+                    var advance = function () {
+                        var start = cursor.from(),
+                            match
+                        if (!(match = cursor.findNext())) {
+                            cursor = getSearchCursor(cm, query)
+                            if (!(match = cursor.findNext()) || (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return
+                        }
+                        cm.setSelection(cursor.from(), cursor.to())
+                        cm.scrollIntoView({ from: cursor.from(), to: cursor.to() })
+                        confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase('Replace?'), [
+                            function () {
+                                doReplace(match)
+                            },
+                            advance,
+                            function () {
+                                replaceAll(cm, query, text)
+                            },
+                        ])
+                    }
+                    var doReplace = function (match) {
+                        cursor.replace(
+                            typeof query == 'string'
+                                ? text
+                                : text.replace(/\$(\d)/g, function (_, i) {
+                                      return match[i]
+                                  })
+                        )
+                        advance()
+                    }
+                    advance()
+                }
+            })
+        })
+    }
+
+    CodeMirror.commands.find = function (cm) {
+        clearSearch(cm)
+        doSearch(cm)
+    }
+    CodeMirror.commands.findPersistent = function (cm) {
+        clearSearch(cm)
+        doSearch(cm, false, true)
+    }
+    CodeMirror.commands.findPersistentNext = function (cm) {
+        doSearch(cm, false, true, true)
+    }
+    CodeMirror.commands.findPersistentPrev = function (cm) {
+        doSearch(cm, true, true, true)
+    }
+    CodeMirror.commands.findNext = doSearch
+    CodeMirror.commands.findPrev = function (cm) {
+        doSearch(cm, true)
+    }
+    CodeMirror.commands.clearSearch = clearSearch
+    CodeMirror.commands.replace = replace
+    CodeMirror.commands.replaceAll = function (cm) {
+        replace(cm, true)
+    }
+})

+ 314 - 0
libs/codemirror/addon/search/searchcursor.js

@@ -0,0 +1,314 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+    var Pos = CodeMirror.Pos
+
+    function regexpFlags(regexp) {
+        var flags = regexp.flags
+        return flags != null ? flags : (regexp.ignoreCase ? 'i' : '') + (regexp.global ? 'g' : '') + (regexp.multiline ? 'm' : '')
+    }
+
+    function ensureFlags(regexp, flags) {
+        var current = regexpFlags(regexp),
+            target = current
+        for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1) target += flags.charAt(i)
+        return current == target ? regexp : new RegExp(regexp.source, target)
+    }
+
+    function maybeMultiline(regexp) {
+        return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
+    }
+
+    function searchRegexpForward(doc, regexp, start) {
+        regexp = ensureFlags(regexp, 'g')
+        for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
+            regexp.lastIndex = ch
+            var string = doc.getLine(line),
+                match = regexp.exec(string)
+            if (match) return { from: Pos(line, match.index), to: Pos(line, match.index + match[0].length), match: match }
+        }
+    }
+
+    function searchRegexpForwardMultiline(doc, regexp, start) {
+        if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
+
+        regexp = ensureFlags(regexp, 'gm')
+        var string,
+            chunk = 1
+        for (var line = start.line, last = doc.lastLine(); line <= last; ) {
+            // This grows the search buffer in exponentially-sized chunks
+            // between matches, so that nearby matches are fast and don't
+            // require concatenating the whole document (in case we're
+            // searching for something that has tons of matches), but at the
+            // same time, the amount of retries is limited.
+            for (var i = 0; i < chunk; i++) {
+                if (line > last) break
+                var curLine = doc.getLine(line++)
+                string = string == null ? curLine : string + '\n' + curLine
+            }
+            chunk = chunk * 2
+            regexp.lastIndex = start.ch
+            var match = regexp.exec(string)
+            if (match) {
+                var before = string.slice(0, match.index).split('\n'),
+                    inside = match[0].split('\n')
+                var startLine = start.line + before.length - 1,
+                    startCh = before[before.length - 1].length
+                return { from: Pos(startLine, startCh), to: Pos(startLine + inside.length - 1, inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length), match: match }
+            }
+        }
+    }
+
+    function lastMatchIn(string, regexp, endMargin) {
+        var match,
+            from = 0
+        while (from <= string.length) {
+            regexp.lastIndex = from
+            var newMatch = regexp.exec(string)
+            if (!newMatch) break
+            var end = newMatch.index + newMatch[0].length
+            if (end > string.length - endMargin) break
+            if (!match || end > match.index + match[0].length) match = newMatch
+            from = newMatch.index + 1
+        }
+        return match
+    }
+
+    function searchRegexpBackward(doc, regexp, start) {
+        regexp = ensureFlags(regexp, 'g')
+        for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
+            var string = doc.getLine(line)
+            var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)
+            if (match) return { from: Pos(line, match.index), to: Pos(line, match.index + match[0].length), match: match }
+        }
+    }
+
+    function searchRegexpBackwardMultiline(doc, regexp, start) {
+        if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)
+        regexp = ensureFlags(regexp, 'gm')
+        var string,
+            chunkSize = 1,
+            endMargin = doc.getLine(start.line).length - start.ch
+        for (var line = start.line, first = doc.firstLine(); line >= first; ) {
+            for (var i = 0; i < chunkSize && line >= first; i++) {
+                var curLine = doc.getLine(line--)
+                string = string == null ? curLine : curLine + '\n' + string
+            }
+            chunkSize *= 2
+
+            var match = lastMatchIn(string, regexp, endMargin)
+            if (match) {
+                var before = string.slice(0, match.index).split('\n'),
+                    inside = match[0].split('\n')
+                var startLine = line + before.length,
+                    startCh = before[before.length - 1].length
+                return { from: Pos(startLine, startCh), to: Pos(startLine + inside.length - 1, inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length), match: match }
+            }
+        }
+    }
+
+    var doFold, noFold
+    if (String.prototype.normalize) {
+        doFold = function (str) {
+            return str.normalize('NFD').toLowerCase()
+        }
+        noFold = function (str) {
+            return str.normalize('NFD')
+        }
+    } else {
+        doFold = function (str) {
+            return str.toLowerCase()
+        }
+        noFold = function (str) {
+            return str
+        }
+    }
+
+    // Maps a position in a case-folded line back to a position in the original line
+    // (compensating for codepoints increasing in number during folding)
+    function adjustPos(orig, folded, pos, foldFunc) {
+        if (orig.length == folded.length) return pos
+        for (var min = 0, max = pos + Math.max(0, orig.length - folded.length); ; ) {
+            if (min == max) return min
+            var mid = (min + max) >> 1
+            var len = foldFunc(orig.slice(0, mid)).length
+            if (len == pos) return mid
+            else if (len > pos) max = mid
+            else min = mid + 1
+        }
+    }
+
+    function searchStringForward(doc, query, start, caseFold) {
+        // Empty string would match anything and never progress, so we
+        // define it to match nothing instead.
+        if (!query.length) return null
+        var fold = caseFold ? doFold : noFold
+        var lines = fold(query).split(/\r|\n\r?/)
+
+        search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
+            var orig = doc.getLine(line).slice(ch),
+                string = fold(orig)
+            if (lines.length == 1) {
+                var found = string.indexOf(lines[0])
+                if (found == -1) continue search
+                var start = adjustPos(orig, string, found, fold) + ch
+                return { from: Pos(line, adjustPos(orig, string, found, fold) + ch), to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch) }
+            } else {
+                var cutFrom = string.length - lines[0].length
+                if (string.slice(cutFrom) != lines[0]) continue search
+                for (var i = 1; i < lines.length - 1; i++) if (fold(doc.getLine(line + i)) != lines[i]) continue search
+                var end = doc.getLine(line + lines.length - 1),
+                    endString = fold(end),
+                    lastLine = lines[lines.length - 1]
+                if (endString.slice(0, lastLine.length) != lastLine) continue search
+                return { from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch), to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold)) }
+            }
+        }
+    }
+
+    function searchStringBackward(doc, query, start, caseFold) {
+        if (!query.length) return null
+        var fold = caseFold ? doFold : noFold
+        var lines = fold(query).split(/\r|\n\r?/)
+
+        search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
+            var orig = doc.getLine(line)
+            if (ch > -1) orig = orig.slice(0, ch)
+            var string = fold(orig)
+            if (lines.length == 1) {
+                var found = string.lastIndexOf(lines[0])
+                if (found == -1) continue search
+                return { from: Pos(line, adjustPos(orig, string, found, fold)), to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold)) }
+            } else {
+                var lastLine = lines[lines.length - 1]
+                if (string.slice(0, lastLine.length) != lastLine) continue search
+                for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++) if (fold(doc.getLine(start + i)) != lines[i]) continue search
+                var top = doc.getLine(line + 1 - lines.length),
+                    topString = fold(top)
+                if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
+                return { from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)), to: Pos(line, adjustPos(orig, string, lastLine.length, fold)) }
+            }
+        }
+    }
+
+    function SearchCursor(doc, query, pos, options) {
+        this.atOccurrence = false
+        this.afterEmptyMatch = false
+        this.doc = doc
+        pos = pos ? doc.clipPos(pos) : Pos(0, 0)
+        this.pos = { from: pos, to: pos }
+
+        var caseFold
+        if (typeof options == 'object') {
+            caseFold = options.caseFold
+        } else {
+            // Backwards compat for when caseFold was the 4th argument
+            caseFold = options
+            options = null
+        }
+
+        if (typeof query == 'string') {
+            if (caseFold == null) caseFold = false
+            this.matches = function (reverse, pos) {
+                return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
+            }
+        } else {
+            query = ensureFlags(query, 'gm')
+            if (!options || options.multiline !== false)
+                this.matches = function (reverse, pos) {
+                    return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
+                }
+            else
+                this.matches = function (reverse, pos) {
+                    return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
+                }
+        }
+    }
+
+    SearchCursor.prototype = {
+        findNext: function () {
+            return this.find(false)
+        },
+        findPrevious: function () {
+            return this.find(true)
+        },
+
+        find: function (reverse) {
+            var head = this.doc.clipPos(reverse ? this.pos.from : this.pos.to)
+            if (this.afterEmptyMatch && this.atOccurrence) {
+                // do not return the same 0 width match twice
+                head = Pos(head.line, head.ch)
+                if (reverse) {
+                    head.ch--
+                    if (head.ch < 0) {
+                        head.line--
+                        head.ch = (this.doc.getLine(head.line) || '').length
+                    }
+                } else {
+                    head.ch++
+                    if (head.ch > (this.doc.getLine(head.line) || '').length) {
+                        head.ch = 0
+                        head.line++
+                    }
+                }
+                if (CodeMirror.cmpPos(head, this.doc.clipPos(head)) != 0) {
+                    return (this.atOccurrence = false)
+                }
+            }
+            var result = this.matches(reverse, head)
+            this.afterEmptyMatch = result && CodeMirror.cmpPos(result.from, result.to) == 0
+
+            if (result) {
+                this.pos = result
+                this.atOccurrence = true
+                return this.pos.match || true
+            } else {
+                var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
+                this.pos = { from: end, to: end }
+                return (this.atOccurrence = false)
+            }
+        },
+
+        from: function () {
+            if (this.atOccurrence) return this.pos.from
+        },
+        to: function () {
+            if (this.atOccurrence) return this.pos.to
+        },
+
+        replace: function (newText, origin) {
+            if (!this.atOccurrence) return
+            var lines = CodeMirror.splitLines(newText)
+            this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
+            this.pos.to = Pos(this.pos.from.line + lines.length - 1, lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
+        },
+    }
+
+    CodeMirror.defineExtension('getSearchCursor', function (query, pos, caseFold) {
+        return new SearchCursor(this.doc, query, pos, caseFold)
+    })
+    CodeMirror.defineDocExtension('getSearchCursor', function (query, pos, caseFold) {
+        return new SearchCursor(this, query, pos, caseFold)
+    })
+
+    CodeMirror.defineExtension('selectMatches', function (query, caseFold) {
+        var ranges = []
+        var cur = this.getSearchCursor(query, this.getCursor('from'), caseFold)
+        while (cur.findNext()) {
+            if (CodeMirror.cmpPos(cur.to(), this.getCursor('to')) > 0) break
+            ranges.push({ anchor: cur.from(), head: cur.to() })
+        }
+        if (ranges.length) this.setSelections(ranges, 0)
+    })
+})

+ 72 - 0
libs/codemirror/addon/selection/active-line.js

@@ -0,0 +1,72 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+    var WRAP_CLASS = 'CodeMirror-activeline'
+    var BACK_CLASS = 'CodeMirror-activeline-background'
+    var GUTT_CLASS = 'CodeMirror-activeline-gutter'
+
+    CodeMirror.defineOption('styleActiveLine', false, function (cm, val, old) {
+        var prev = old == CodeMirror.Init ? false : old
+        if (val == prev) return
+        if (prev) {
+            cm.off('beforeSelectionChange', selectionChange)
+            clearActiveLines(cm)
+            delete cm.state.activeLines
+        }
+        if (val) {
+            cm.state.activeLines = []
+            updateActiveLines(cm, cm.listSelections())
+            cm.on('beforeSelectionChange', selectionChange)
+        }
+    })
+
+    function clearActiveLines(cm) {
+        for (var i = 0; i < cm.state.activeLines.length; i++) {
+            cm.removeLineClass(cm.state.activeLines[i], 'wrap', WRAP_CLASS)
+            cm.removeLineClass(cm.state.activeLines[i], 'background', BACK_CLASS)
+            cm.removeLineClass(cm.state.activeLines[i], 'gutter', GUTT_CLASS)
+        }
+    }
+
+    function sameArray(a, b) {
+        if (a.length != b.length) return false
+        for (var i = 0; i < a.length; i++) if (a[i] != b[i]) return false
+        return true
+    }
+
+    function updateActiveLines(cm, ranges) {
+        var active = []
+        for (var i = 0; i < ranges.length; i++) {
+            var range = ranges[i]
+            var option = cm.getOption('styleActiveLine')
+            if (typeof option == 'object' && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty()) continue
+            var line = cm.getLineHandleVisualStart(range.head.line)
+            if (active[active.length - 1] != line) active.push(line)
+        }
+        if (sameArray(cm.state.activeLines, active)) return
+        cm.operation(function () {
+            clearActiveLines(cm)
+            for (var i = 0; i < active.length; i++) {
+                cm.addLineClass(active[i], 'wrap', WRAP_CLASS)
+                cm.addLineClass(active[i], 'background', BACK_CLASS)
+                cm.addLineClass(active[i], 'gutter', GUTT_CLASS)
+            }
+            cm.state.activeLines = active
+        })
+    }
+
+    function selectionChange(cm, sel) {
+        updateActiveLines(cm, sel.ranges)
+    }
+})

+ 125 - 0
libs/codemirror/addon/selection/mark-selection.js

@@ -0,0 +1,125 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Because sometimes you need to mark the selected *text*.
+//
+// Adds an option 'styleSelectedText' which, when enabled, gives
+// selected text the CSS class given as option value, or
+// "CodeMirror-selectedtext" when the value is not a string.
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineOption('styleSelectedText', false, function (cm, val, old) {
+        var prev = old && old != CodeMirror.Init
+        if (val && !prev) {
+            cm.state.markedSelection = []
+            cm.state.markedSelectionStyle = typeof val == 'string' ? val : 'CodeMirror-selectedtext'
+            reset(cm)
+            cm.on('cursorActivity', onCursorActivity)
+            cm.on('change', onChange)
+        } else if (!val && prev) {
+            cm.off('cursorActivity', onCursorActivity)
+            cm.off('change', onChange)
+            clear(cm)
+            cm.state.markedSelection = cm.state.markedSelectionStyle = null
+        }
+    })
+
+    function onCursorActivity(cm) {
+        if (cm.state.markedSelection)
+            cm.operation(function () {
+                update(cm)
+            })
+    }
+
+    function onChange(cm) {
+        if (cm.state.markedSelection && cm.state.markedSelection.length)
+            cm.operation(function () {
+                clear(cm)
+            })
+    }
+
+    var CHUNK_SIZE = 8
+    var Pos = CodeMirror.Pos
+    var cmp = CodeMirror.cmpPos
+
+    function coverRange(cm, from, to, addAt) {
+        if (cmp(from, to) == 0) return
+        var array = cm.state.markedSelection
+        var cls = cm.state.markedSelectionStyle
+        for (var line = from.line; ; ) {
+            var start = line == from.line ? from : Pos(line, 0)
+            var endLine = line + CHUNK_SIZE,
+                atEnd = endLine >= to.line
+            var end = atEnd ? to : Pos(endLine, 0)
+            var mark = cm.markText(start, end, { className: cls })
+            if (addAt == null) array.push(mark)
+            else array.splice(addAt++, 0, mark)
+            if (atEnd) break
+            line = endLine
+        }
+    }
+
+    function clear(cm) {
+        var array = cm.state.markedSelection
+        for (var i = 0; i < array.length; ++i) array[i].clear()
+        array.length = 0
+    }
+
+    function reset(cm) {
+        clear(cm)
+        var ranges = cm.listSelections()
+        for (var i = 0; i < ranges.length; i++) coverRange(cm, ranges[i].from(), ranges[i].to())
+    }
+
+    function update(cm) {
+        if (!cm.somethingSelected()) return clear(cm)
+        if (cm.listSelections().length > 1) return reset(cm)
+
+        var from = cm.getCursor('start'),
+            to = cm.getCursor('end')
+
+        var array = cm.state.markedSelection
+        if (!array.length) return coverRange(cm, from, to)
+
+        var coverStart = array[0].find(),
+            coverEnd = array[array.length - 1].find()
+        if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE || cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0) return reset(cm)
+
+        while (cmp(from, coverStart.from) > 0) {
+            array.shift().clear()
+            coverStart = array[0].find()
+        }
+        if (cmp(from, coverStart.from) < 0) {
+            if (coverStart.to.line - from.line < CHUNK_SIZE) {
+                array.shift().clear()
+                coverRange(cm, from, coverStart.to, 0)
+            } else {
+                coverRange(cm, from, coverStart.from, 0)
+            }
+        }
+
+        while (cmp(to, coverEnd.to) < 0) {
+            array.pop().clear()
+            coverEnd = array[array.length - 1].find()
+        }
+        if (cmp(to, coverEnd.to) > 0) {
+            if (to.line - coverEnd.from.line < CHUNK_SIZE) {
+                array.pop().clear()
+                coverRange(cm, coverEnd.from, to)
+            } else {
+                coverRange(cm, coverEnd.to, to)
+            }
+        }
+    }
+})

+ 104 - 0
libs/codemirror/addon/selection/selection-pointer.js

@@ -0,0 +1,104 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineOption('selectionPointer', false, function (cm, val) {
+        var data = cm.state.selectionPointer
+        if (data) {
+            CodeMirror.off(cm.getWrapperElement(), 'mousemove', data.mousemove)
+            CodeMirror.off(cm.getWrapperElement(), 'mouseout', data.mouseout)
+            CodeMirror.off(window, 'scroll', data.windowScroll)
+            cm.off('cursorActivity', reset)
+            cm.off('scroll', reset)
+            cm.state.selectionPointer = null
+            cm.display.lineDiv.style.cursor = ''
+        }
+        if (val) {
+            data = cm.state.selectionPointer = {
+                value: typeof val == 'string' ? val : 'default',
+                mousemove: function (event) {
+                    mousemove(cm, event)
+                },
+                mouseout: function (event) {
+                    mouseout(cm, event)
+                },
+                windowScroll: function () {
+                    reset(cm)
+                },
+                rects: null,
+                mouseX: null,
+                mouseY: null,
+                willUpdate: false,
+            }
+            CodeMirror.on(cm.getWrapperElement(), 'mousemove', data.mousemove)
+            CodeMirror.on(cm.getWrapperElement(), 'mouseout', data.mouseout)
+            CodeMirror.on(window, 'scroll', data.windowScroll)
+            cm.on('cursorActivity', reset)
+            cm.on('scroll', reset)
+        }
+    })
+
+    function mousemove(cm, event) {
+        var data = cm.state.selectionPointer
+        if (event.buttons == null ? event.which : event.buttons) {
+            data.mouseX = data.mouseY = null
+        } else {
+            data.mouseX = event.clientX
+            data.mouseY = event.clientY
+        }
+        scheduleUpdate(cm)
+    }
+
+    function mouseout(cm, event) {
+        if (!cm.getWrapperElement().contains(event.relatedTarget)) {
+            var data = cm.state.selectionPointer
+            data.mouseX = data.mouseY = null
+            scheduleUpdate(cm)
+        }
+    }
+
+    function reset(cm) {
+        cm.state.selectionPointer.rects = null
+        scheduleUpdate(cm)
+    }
+
+    function scheduleUpdate(cm) {
+        if (!cm.state.selectionPointer.willUpdate) {
+            cm.state.selectionPointer.willUpdate = true
+            setTimeout(function () {
+                update(cm)
+                cm.state.selectionPointer.willUpdate = false
+            }, 50)
+        }
+    }
+
+    function update(cm) {
+        var data = cm.state.selectionPointer
+        if (!data) return
+        if (data.rects == null && data.mouseX != null) {
+            data.rects = []
+            if (cm.somethingSelected()) {
+                for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling) data.rects.push(sel.getBoundingClientRect())
+            }
+        }
+        var inside = false
+        if (data.mouseX != null)
+            for (var i = 0; i < data.rects.length; i++) {
+                var rect = data.rects[i]
+                if (rect.left <= data.mouseX && rect.right >= data.mouseX && rect.top <= data.mouseY && rect.bottom >= data.mouseY) inside = true
+            }
+        var cursor = inside ? data.value : ''
+        if (cm.display.lineDiv.style.cursor != cursor) cm.display.lineDiv.style.cursor = cursor
+    }
+})

+ 87 - 0
libs/codemirror/addon/tern/tern.css

@@ -0,0 +1,87 @@
+.CodeMirror-Tern-completion {
+  padding-left: 22px;
+  position: relative;
+  line-height: 1.5;
+}
+.CodeMirror-Tern-completion:before {
+  position: absolute;
+  left: 2px;
+  bottom: 2px;
+  border-radius: 50%;
+  font-size: 12px;
+  font-weight: bold;
+  height: 15px;
+  width: 15px;
+  line-height: 16px;
+  text-align: center;
+  color: white;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.CodeMirror-Tern-completion-unknown:before {
+  content: "?";
+  background: #4bb;
+}
+.CodeMirror-Tern-completion-object:before {
+  content: "O";
+  background: #77c;
+}
+.CodeMirror-Tern-completion-fn:before {
+  content: "F";
+  background: #7c7;
+}
+.CodeMirror-Tern-completion-array:before {
+  content: "A";
+  background: #c66;
+}
+.CodeMirror-Tern-completion-number:before {
+  content: "1";
+  background: #999;
+}
+.CodeMirror-Tern-completion-string:before {
+  content: "S";
+  background: #999;
+}
+.CodeMirror-Tern-completion-bool:before {
+  content: "B";
+  background: #999;
+}
+
+.CodeMirror-Tern-completion-guess {
+  color: #999;
+}
+
+.CodeMirror-Tern-tooltip {
+  border: 1px solid silver;
+  border-radius: 3px;
+  color: #444;
+  padding: 2px 5px;
+  font-size: 90%;
+  font-family: monospace;
+  background-color: white;
+  white-space: pre-wrap;
+
+  max-width: 40em;
+  position: absolute;
+  z-index: 10;
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+
+  transition: opacity 1s;
+  -moz-transition: opacity 1s;
+  -webkit-transition: opacity 1s;
+  -o-transition: opacity 1s;
+  -ms-transition: opacity 1s;
+}
+
+.CodeMirror-Tern-hint-doc {
+  max-width: 25em;
+  margin-top: -3px;
+}
+
+.CodeMirror-Tern-fname { color: black; }
+.CodeMirror-Tern-farg { color: #70a; }
+.CodeMirror-Tern-farg-current { text-decoration: underline; }
+.CodeMirror-Tern-type { color: #07c; }
+.CodeMirror-Tern-fhint-guess { opacity: .7; }

+ 798 - 0
libs/codemirror/addon/tern/tern.js

@@ -0,0 +1,798 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Glue code between CodeMirror and Tern.
+//
+// Create a CodeMirror.TernServer to wrap an actual Tern server,
+// register open documents (CodeMirror.Doc instances) with it, and
+// call its methods to activate the assisting functions that Tern
+// provides.
+//
+// Options supported (all optional):
+// * defs: An array of JSON definition data structures.
+// * plugins: An object mapping plugin names to configuration
+//   options.
+// * getFile: A function(name, c) that can be used to access files in
+//   the project that haven't been loaded yet. Simply do c(null) to
+//   indicate that a file is not available.
+// * fileFilter: A function(value, docName, doc) that will be applied
+//   to documents before passing them on to Tern.
+// * switchToDoc: A function(name, doc) that should, when providing a
+//   multi-file view, switch the view or focus to the named file.
+// * showError: A function(editor, message) that can be used to
+//   override the way errors are displayed.
+// * completionTip: Customize the content in tooltips for completions.
+//   Is passed a single argument—the completion's data as returned by
+//   Tern—and may return a string, DOM node, or null to indicate that
+//   no tip should be shown. By default the docstring is shown.
+// * typeTip: Like completionTip, but for the tooltips shown for type
+//   queries.
+// * responseFilter: A function(doc, query, request, error, data) that
+//   will be applied to the Tern responses before treating them
+//
+//
+// It is possible to run the Tern server in a web worker by specifying
+// these additional options:
+// * useWorker: Set to true to enable web worker mode. You'll probably
+//   want to feature detect the actual value you use here, for example
+//   !!window.Worker.
+// * workerScript: The main script of the worker. Point this to
+//   wherever you are hosting worker.js from this directory.
+// * workerDeps: An array of paths pointing (relative to workerScript)
+//   to the Acorn and Tern libraries and any Tern plugins you want to
+//   load. Or, if you minified those into a single script and included
+//   them in the workerScript, simply leave this undefined.
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+    // declare global: tern
+
+    CodeMirror.TernServer = function (options) {
+        var self = this
+        this.options = options || {}
+        var plugins = this.options.plugins || (this.options.plugins = {})
+        if (!plugins.doc_comment) plugins.doc_comment = true
+        this.docs = Object.create(null)
+        if (this.options.useWorker) {
+            this.server = new WorkerServer(this)
+        } else {
+            this.server = new tern.Server({
+                getFile: function (name, c) {
+                    return getFile(self, name, c)
+                },
+                async: true,
+                defs: this.options.defs || [],
+                plugins: plugins,
+            })
+        }
+        this.trackChange = function (doc, change) {
+            trackChange(self, doc, change)
+        }
+
+        this.cachedArgHints = null
+        this.activeArgHints = null
+        this.jumpStack = []
+
+        this.getHint = function (cm, c) {
+            return hint(self, cm, c)
+        }
+        this.getHint.async = true
+    }
+
+    CodeMirror.TernServer.prototype = {
+        addDoc: function (name, doc) {
+            var data = { doc: doc, name: name, changed: null }
+            this.server.addFile(name, docValue(this, data))
+            CodeMirror.on(doc, 'change', this.trackChange)
+            return (this.docs[name] = data)
+        },
+
+        delDoc: function (id) {
+            var found = resolveDoc(this, id)
+            if (!found) return
+            CodeMirror.off(found.doc, 'change', this.trackChange)
+            delete this.docs[found.name]
+            this.server.delFile(found.name)
+        },
+
+        hideDoc: function (id) {
+            closeArgHints(this)
+            var found = resolveDoc(this, id)
+            if (found && found.changed) sendDoc(this, found)
+        },
+
+        complete: function (cm) {
+            cm.showHint({ hint: this.getHint })
+        },
+
+        showType: function (cm, pos, c) {
+            showContextInfo(this, cm, pos, 'type', c)
+        },
+
+        showDocs: function (cm, pos, c) {
+            showContextInfo(this, cm, pos, 'documentation', c)
+        },
+
+        updateArgHints: function (cm) {
+            updateArgHints(this, cm)
+        },
+
+        jumpToDef: function (cm) {
+            jumpToDef(this, cm)
+        },
+
+        jumpBack: function (cm) {
+            jumpBack(this, cm)
+        },
+
+        rename: function (cm) {
+            rename(this, cm)
+        },
+
+        selectName: function (cm) {
+            selectName(this, cm)
+        },
+
+        request: function (cm, query, c, pos) {
+            var self = this
+            var doc = findDoc(this, cm.getDoc())
+            var request = buildRequest(this, doc, query, pos)
+            var extraOptions = request.query && this.options.queryOptions && this.options.queryOptions[request.query.type]
+            if (extraOptions) for (var prop in extraOptions) request.query[prop] = extraOptions[prop]
+
+            this.server.request(request, function (error, data) {
+                if (!error && self.options.responseFilter) data = self.options.responseFilter(doc, query, request, error, data)
+                c(error, data)
+            })
+        },
+
+        destroy: function () {
+            closeArgHints(this)
+            if (this.worker) {
+                this.worker.terminate()
+                this.worker = null
+            }
+        },
+    }
+
+    var Pos = CodeMirror.Pos
+    var cls = 'CodeMirror-Tern-'
+    var bigDoc = 250
+
+    function getFile(ts, name, c) {
+        var buf = ts.docs[name]
+        if (buf) c(docValue(ts, buf))
+        else if (ts.options.getFile) ts.options.getFile(name, c)
+        else c(null)
+    }
+
+    function findDoc(ts, doc, name) {
+        for (var n in ts.docs) {
+            var cur = ts.docs[n]
+            if (cur.doc == doc) return cur
+        }
+        if (!name)
+            for (var i = 0; ; ++i) {
+                n = '[doc' + (i || '') + ']'
+                if (!ts.docs[n]) {
+                    name = n
+                    break
+                }
+            }
+        return ts.addDoc(name, doc)
+    }
+
+    function resolveDoc(ts, id) {
+        if (typeof id == 'string') return ts.docs[id]
+        if (id instanceof CodeMirror) id = id.getDoc()
+        if (id instanceof CodeMirror.Doc) return findDoc(ts, id)
+    }
+
+    function trackChange(ts, doc, change) {
+        var data = findDoc(ts, doc)
+
+        var argHints = ts.cachedArgHints
+        if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0) ts.cachedArgHints = null
+
+        var changed = data.changed
+        if (changed == null) data.changed = changed = { from: change.from.line, to: change.from.line }
+        var end = change.from.line + (change.text.length - 1)
+        if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end)
+        if (end >= changed.to) changed.to = end + 1
+        if (changed.from > change.from.line) changed.from = change.from.line
+
+        if (doc.lineCount() > bigDoc && change.to - changed.from > 100)
+            setTimeout(function () {
+                if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data)
+            }, 200)
+    }
+
+    function sendDoc(ts, doc) {
+        ts.server.request({ files: [{ type: 'full', name: doc.name, text: docValue(ts, doc) }] }, function (error) {
+            if (error) window.console.error(error)
+            else doc.changed = null
+        })
+    }
+
+    // Completion
+
+    function hint(ts, cm, c) {
+        ts.request(cm, { type: 'completions', types: true, docs: true, urls: true }, function (error, data) {
+            if (error) return showError(ts, cm, error)
+            var completions = [],
+                after = ''
+            var from = data.start,
+                to = data.end
+            if (cm.getRange(Pos(from.line, from.ch - 2), from) == '["' && cm.getRange(to, Pos(to.line, to.ch + 2)) != '"]') after = '"]'
+
+            for (var i = 0; i < data.completions.length; ++i) {
+                var completion = data.completions[i],
+                    className = typeToIcon(completion.type)
+                if (data.guess) className += ' ' + cls + 'guess'
+                completions.push({ text: completion.name + after, displayText: completion.displayName || completion.name, className: className, data: completion })
+            }
+
+            var obj = { from: from, to: to, list: completions }
+            var tooltip = null
+            CodeMirror.on(obj, 'close', function () {
+                remove(tooltip)
+            })
+            CodeMirror.on(obj, 'update', function () {
+                remove(tooltip)
+            })
+            CodeMirror.on(obj, 'select', function (cur, node) {
+                remove(tooltip)
+                var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc
+                if (content) {
+                    tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset, node.getBoundingClientRect().top + window.pageYOffset, content, cm, cls + 'hint-doc')
+                }
+            })
+            c(obj)
+        })
+    }
+
+    function typeToIcon(type) {
+        var suffix
+        if (type == '?') suffix = 'unknown'
+        else if (type == 'number' || type == 'string' || type == 'bool') suffix = type
+        else if (/^fn\(/.test(type)) suffix = 'fn'
+        else if (/^\[/.test(type)) suffix = 'array'
+        else suffix = 'object'
+        return cls + 'completion ' + cls + 'completion-' + suffix
+    }
+
+    // Type queries
+
+    function showContextInfo(ts, cm, pos, queryName, c) {
+        ts.request(
+            cm,
+            queryName,
+            function (error, data) {
+                if (error) return showError(ts, cm, error)
+                if (ts.options.typeTip) {
+                    var tip = ts.options.typeTip(data)
+                } else {
+                    var tip = elt('span', null, elt('strong', null, data.type || 'not found'))
+                    if (data.doc) tip.appendChild(document.createTextNode(' — ' + data.doc))
+                    if (data.url) {
+                        tip.appendChild(document.createTextNode(' '))
+                        var child = tip.appendChild(elt('a', null, '[docs]'))
+                        child.href = data.url
+                        child.target = '_blank'
+                    }
+                }
+                tempTooltip(cm, tip, ts)
+                if (c) c()
+            },
+            pos
+        )
+    }
+
+    // Maintaining argument hints
+
+    function updateArgHints(ts, cm) {
+        closeArgHints(ts)
+
+        if (cm.somethingSelected()) return
+        var state = cm.getTokenAt(cm.getCursor()).state
+        var inner = CodeMirror.innerMode(cm.getMode(), state)
+        if (inner.mode.name != 'javascript') return
+        var lex = inner.state.lexical
+        if (lex.info != 'call') return
+
+        var ch,
+            argPos = lex.pos || 0,
+            tabSize = cm.getOption('tabSize')
+        for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
+            var str = cm.getLine(line),
+                extra = 0
+            for (var pos = 0; ; ) {
+                var tab = str.indexOf('\t', pos)
+                if (tab == -1) break
+                extra += tabSize - ((tab + extra) % tabSize) - 1
+                pos = tab + 1
+            }
+            ch = lex.column - extra
+            if (str.charAt(ch) == '(') {
+                found = true
+                break
+            }
+        }
+        if (!found) return
+
+        var start = Pos(line, ch)
+        var cache = ts.cachedArgHints
+        if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0) return showArgHints(ts, cm, argPos)
+
+        ts.request(cm, { type: 'type', preferFunction: true, end: start }, function (error, data) {
+            if (error || !data.type || !/^fn\(/.test(data.type)) return
+            ts.cachedArgHints = {
+                start: start,
+                type: parseFnType(data.type),
+                name: data.exprName || data.name || 'fn',
+                guess: data.guess,
+                doc: cm.getDoc(),
+            }
+            showArgHints(ts, cm, argPos)
+        })
+    }
+
+    function showArgHints(ts, cm, pos) {
+        closeArgHints(ts)
+
+        var cache = ts.cachedArgHints,
+            tp = cache.type
+        var tip = elt('span', cache.guess ? cls + 'fhint-guess' : null, elt('span', cls + 'fname', cache.name), '(')
+        for (var i = 0; i < tp.args.length; ++i) {
+            if (i) tip.appendChild(document.createTextNode(', '))
+            var arg = tp.args[i]
+            tip.appendChild(elt('span', cls + 'farg' + (i == pos ? ' ' + cls + 'farg-current' : ''), arg.name || '?'))
+            if (arg.type != '?') {
+                tip.appendChild(document.createTextNode(':\u00a0'))
+                tip.appendChild(elt('span', cls + 'type', arg.type))
+            }
+        }
+        tip.appendChild(document.createTextNode(tp.rettype ? ') ->\u00a0' : ')'))
+        if (tp.rettype) tip.appendChild(elt('span', cls + 'type', tp.rettype))
+        var place = cm.cursorCoords(null, 'page')
+        var tooltip = (ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip, cm))
+        setTimeout(function () {
+            tooltip.clear = onEditorActivity(cm, function () {
+                if (ts.activeArgHints == tooltip) closeArgHints(ts)
+            })
+        }, 20)
+    }
+
+    function parseFnType(text) {
+        var args = [],
+            pos = 3
+
+        function skipMatching(upto) {
+            var depth = 0,
+                start = pos
+            for (;;) {
+                var next = text.charAt(pos)
+                if (upto.test(next) && !depth) return text.slice(start, pos)
+                if (/[{\[\(]/.test(next)) ++depth
+                else if (/[}\]\)]/.test(next)) --depth
+                ++pos
+            }
+        }
+
+        // Parse arguments
+        if (text.charAt(pos) != ')')
+            for (;;) {
+                var name = text.slice(pos).match(/^([^, \(\[\{]+): /)
+                if (name) {
+                    pos += name[0].length
+                    name = name[1]
+                }
+                args.push({ name: name, type: skipMatching(/[\),]/) })
+                if (text.charAt(pos) == ')') break
+                pos += 2
+            }
+
+        var rettype = text.slice(pos).match(/^\) -> (.*)$/)
+
+        return { args: args, rettype: rettype && rettype[1] }
+    }
+
+    // Moving to the definition of something
+
+    function jumpToDef(ts, cm) {
+        function inner(varName) {
+            var req = { type: 'definition', variable: varName || null }
+            var doc = findDoc(ts, cm.getDoc())
+            ts.server.request(buildRequest(ts, doc, req), function (error, data) {
+                if (error) return showError(ts, cm, error)
+                if (!data.file && data.url) {
+                    window.open(data.url)
+                    return
+                }
+
+                if (data.file) {
+                    var localDoc = ts.docs[data.file],
+                        found
+                    if (localDoc && (found = findContext(localDoc.doc, data))) {
+                        ts.jumpStack.push({ file: doc.name, start: cm.getCursor('from'), end: cm.getCursor('to') })
+                        moveTo(ts, doc, localDoc, found.start, found.end)
+                        return
+                    }
+                }
+                showError(ts, cm, 'Could not find a definition.')
+            })
+        }
+
+        if (!atInterestingExpression(cm))
+            dialog(cm, 'Jump to variable', function (name) {
+                if (name) inner(name)
+            })
+        else inner()
+    }
+
+    function jumpBack(ts, cm) {
+        var pos = ts.jumpStack.pop(),
+            doc = pos && ts.docs[pos.file]
+        if (!doc) return
+        moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end)
+    }
+
+    function moveTo(ts, curDoc, doc, start, end) {
+        doc.doc.setSelection(start, end)
+        if (curDoc != doc && ts.options.switchToDoc) {
+            closeArgHints(ts)
+            ts.options.switchToDoc(doc.name, doc.doc)
+        }
+    }
+
+    // The {line,ch} representation of positions makes this rather awkward.
+    function findContext(doc, data) {
+        var before = data.context.slice(0, data.contextOffset).split('\n')
+        var startLine = data.start.line - (before.length - 1)
+        var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length)
+
+        var text = doc.getLine(startLine).slice(start.ch)
+        for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur) text += '\n' + doc.getLine(cur)
+        if (text.slice(0, data.context.length) == data.context) return data
+
+        var cursor = doc.getSearchCursor(data.context, 0, false)
+        var nearest,
+            nearestDist = Infinity
+        while (cursor.findNext()) {
+            var from = cursor.from(),
+                dist = Math.abs(from.line - start.line) * 10000
+            if (!dist) dist = Math.abs(from.ch - start.ch)
+            if (dist < nearestDist) {
+                nearest = from
+                nearestDist = dist
+            }
+        }
+        if (!nearest) return null
+
+        if (before.length == 1) nearest.ch += before[0].length
+        else nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length)
+        if (data.start.line == data.end.line) var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch))
+        else var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch)
+        return { start: nearest, end: end }
+    }
+
+    function atInterestingExpression(cm) {
+        var pos = cm.getCursor('end'),
+            tok = cm.getTokenAt(pos)
+        if (tok.start < pos.ch && tok.type == 'comment') return false
+        return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1))
+    }
+
+    // Variable renaming
+
+    function rename(ts, cm) {
+        var token = cm.getTokenAt(cm.getCursor())
+        if (!/\w/.test(token.string)) return showError(ts, cm, 'Not at a variable')
+        dialog(cm, 'New name for ' + token.string, function (newName) {
+            ts.request(cm, { type: 'rename', newName: newName, fullDocs: true }, function (error, data) {
+                if (error) return showError(ts, cm, error)
+                applyChanges(ts, data.changes)
+            })
+        })
+    }
+
+    function selectName(ts, cm) {
+        var name = findDoc(ts, cm.doc).name
+        ts.request(cm, { type: 'refs' }, function (error, data) {
+            if (error) return showError(ts, cm, error)
+            var ranges = [],
+                cur = 0
+            var curPos = cm.getCursor()
+            for (var i = 0; i < data.refs.length; i++) {
+                var ref = data.refs[i]
+                if (ref.file == name) {
+                    ranges.push({ anchor: ref.start, head: ref.end })
+                    if (cmpPos(curPos, ref.start) >= 0 && cmpPos(curPos, ref.end) <= 0) cur = ranges.length - 1
+                }
+            }
+            cm.setSelections(ranges, cur)
+        })
+    }
+
+    var nextChangeOrig = 0
+    function applyChanges(ts, changes) {
+        var perFile = Object.create(null)
+        for (var i = 0; i < changes.length; ++i) {
+            var ch = changes[i]
+            ;(perFile[ch.file] || (perFile[ch.file] = [])).push(ch)
+        }
+        for (var file in perFile) {
+            var known = ts.docs[file],
+                chs = perFile[file]
+            if (!known) continue
+            chs.sort(function (a, b) {
+                return cmpPos(b.start, a.start)
+            })
+            var origin = '*rename' + ++nextChangeOrig
+            for (var i = 0; i < chs.length; ++i) {
+                var ch = chs[i]
+                known.doc.replaceRange(ch.text, ch.start, ch.end, origin)
+            }
+        }
+    }
+
+    // Generic request-building helper
+
+    function buildRequest(ts, doc, query, pos) {
+        var files = [],
+            offsetLines = 0,
+            allowFragments = !query.fullDocs
+        if (!allowFragments) delete query.fullDocs
+        if (typeof query == 'string') query = { type: query }
+        query.lineCharPositions = true
+        if (query.end == null) {
+            query.end = pos || doc.doc.getCursor('end')
+            if (doc.doc.somethingSelected()) query.start = doc.doc.getCursor('start')
+        }
+        var startPos = query.start || query.end
+
+        if (doc.changed) {
+            if (doc.doc.lineCount() > bigDoc && allowFragments !== false && doc.changed.to - doc.changed.from < 100 && doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
+                files.push(getFragmentAround(doc, startPos, query.end))
+                query.file = '#0'
+                var offsetLines = files[0].offsetLines
+                if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch)
+                query.end = Pos(query.end.line - offsetLines, query.end.ch)
+            } else {
+                files.push({ type: 'full', name: doc.name, text: docValue(ts, doc) })
+                query.file = doc.name
+                doc.changed = null
+            }
+        } else {
+            query.file = doc.name
+        }
+        for (var name in ts.docs) {
+            var cur = ts.docs[name]
+            if (cur.changed && cur != doc) {
+                files.push({ type: 'full', name: cur.name, text: docValue(ts, cur) })
+                cur.changed = null
+            }
+        }
+
+        return { query: query, files: files }
+    }
+
+    function getFragmentAround(data, start, end) {
+        var doc = data.doc
+        var minIndent = null,
+            minLine = null,
+            endLine,
+            tabSize = 4
+        for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
+            var line = doc.getLine(p),
+                fn = line.search(/\bfunction\b/)
+            if (fn < 0) continue
+            var indent = CodeMirror.countColumn(line, null, tabSize)
+            if (minIndent != null && minIndent <= indent) continue
+            minIndent = indent
+            minLine = p
+        }
+        if (minLine == null) minLine = min
+        var max = Math.min(doc.lastLine(), end.line + 20)
+        if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize)) endLine = max
+        else
+            for (endLine = end.line + 1; endLine < max; ++endLine) {
+                var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize)
+                if (indent <= minIndent) break
+            }
+        var from = Pos(minLine, 0)
+
+        return { type: 'part', name: data.name, offsetLines: from.line, text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0)) }
+    }
+
+    // Generic utilities
+
+    var cmpPos = CodeMirror.cmpPos
+
+    function elt(tagname, cls /*, ... elts*/) {
+        var e = document.createElement(tagname)
+        if (cls) e.className = cls
+        for (var i = 2; i < arguments.length; ++i) {
+            var elt = arguments[i]
+            if (typeof elt == 'string') elt = document.createTextNode(elt)
+            e.appendChild(elt)
+        }
+        return e
+    }
+
+    function dialog(cm, text, f) {
+        if (cm.openDialog) cm.openDialog(text + ': <input type=text>', f)
+        else f(prompt(text, ''))
+    }
+
+    // Tooltips
+
+    function tempTooltip(cm, content, ts) {
+        if (cm.state.ternTooltip) remove(cm.state.ternTooltip)
+        var where = cm.cursorCoords()
+        var tip = (cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content, cm))
+        function maybeClear() {
+            old = true
+            if (!mouseOnTip) clear()
+        }
+        function clear() {
+            cm.state.ternTooltip = null
+            if (tip.parentNode) fadeOut(tip)
+            clearActivity()
+        }
+        var mouseOnTip = false,
+            old = false
+        CodeMirror.on(tip, 'mousemove', function () {
+            mouseOnTip = true
+        })
+        CodeMirror.on(tip, 'mouseout', function (e) {
+            var related = e.relatedTarget || e.toElement
+            if (!related || !CodeMirror.contains(tip, related)) {
+                if (old) clear()
+                else mouseOnTip = false
+            }
+        })
+        setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700)
+        var clearActivity = onEditorActivity(cm, clear)
+    }
+
+    function onEditorActivity(cm, f) {
+        cm.on('cursorActivity', f)
+        cm.on('blur', f)
+        cm.on('scroll', f)
+        cm.on('setDoc', f)
+        return function () {
+            cm.off('cursorActivity', f)
+            cm.off('blur', f)
+            cm.off('scroll', f)
+            cm.off('setDoc', f)
+        }
+    }
+
+    function makeTooltip(x, y, content, cm, className) {
+        var node = elt('div', cls + 'tooltip' + ' ' + (className || ''), content)
+        node.style.left = x + 'px'
+        node.style.top = y + 'px'
+        var container = ((cm.options || {}).hintOptions || {}).container || document.body
+        container.appendChild(node)
+
+        var pos = cm.cursorCoords()
+        var winW = window.innerWidth
+        var winH = window.innerHeight
+        var box = node.getBoundingClientRect()
+        var hints = document.querySelector('.CodeMirror-hints')
+        var overlapY = box.bottom - winH
+        var overlapX = box.right - winW
+
+        if (hints && overlapX > 0) {
+            node.style.left = 0
+            var box = node.getBoundingClientRect()
+            node.style.left = (x = x - hints.offsetWidth - box.width) + 'px'
+            overlapX = box.right - winW
+        }
+        if (overlapY > 0) {
+            var height = box.bottom - box.top,
+                curTop = pos.top - (pos.bottom - box.top)
+            if (curTop - height > 0) {
+                // Fits above cursor
+                node.style.top = pos.top - height + 'px'
+            } else if (height > winH) {
+                node.style.height = winH - 5 + 'px'
+                node.style.top = pos.bottom - box.top + 'px'
+            }
+        }
+        if (overlapX > 0) {
+            if (box.right - box.left > winW) {
+                node.style.width = winW - 5 + 'px'
+                overlapX -= box.right - box.left - winW
+            }
+            node.style.left = x - overlapX + 'px'
+        }
+
+        return node
+    }
+
+    function remove(node) {
+        var p = node && node.parentNode
+        if (p) p.removeChild(node)
+    }
+
+    function fadeOut(tooltip) {
+        tooltip.style.opacity = '0'
+        setTimeout(function () {
+            remove(tooltip)
+        }, 1100)
+    }
+
+    function showError(ts, cm, msg) {
+        if (ts.options.showError) ts.options.showError(cm, msg)
+        else tempTooltip(cm, String(msg), ts)
+    }
+
+    function closeArgHints(ts) {
+        if (ts.activeArgHints) {
+            if (ts.activeArgHints.clear) ts.activeArgHints.clear()
+            remove(ts.activeArgHints)
+            ts.activeArgHints = null
+        }
+    }
+
+    function docValue(ts, doc) {
+        var val = doc.doc.getValue()
+        if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc)
+        return val
+    }
+
+    // Worker wrapper
+
+    function WorkerServer(ts) {
+        var worker = (ts.worker = new Worker(ts.options.workerScript))
+        worker.postMessage({ type: 'init', defs: ts.options.defs, plugins: ts.options.plugins, scripts: ts.options.workerDeps })
+        var msgId = 0,
+            pending = {}
+
+        function send(data, c) {
+            if (c) {
+                data.id = ++msgId
+                pending[msgId] = c
+            }
+            worker.postMessage(data)
+        }
+        worker.onmessage = function (e) {
+            var data = e.data
+            if (data.type == 'getFile') {
+                getFile(ts, data.name, function (err, text) {
+                    send({ type: 'getFile', err: String(err), text: text, id: data.id })
+                })
+            } else if (data.type == 'debug') {
+                window.console.log(data.message)
+            } else if (data.id && pending[data.id]) {
+                pending[data.id](data.err, data.body)
+                delete pending[data.id]
+            }
+        }
+        worker.onerror = function (e) {
+            for (var id in pending) pending[id](e)
+            pending = {}
+        }
+
+        this.addFile = function (name, text) {
+            send({ type: 'add', name: name, text: text })
+        }
+        this.delFile = function (name) {
+            send({ type: 'del', name: name })
+        }
+        this.request = function (body, c) {
+            send({ type: 'req', body: body }, c)
+        }
+    }
+})

+ 52 - 0
libs/codemirror/addon/tern/worker.js

@@ -0,0 +1,52 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// declare global: tern, server
+
+var server
+
+this.onmessage = function (e) {
+    var data = e.data
+    switch (data.type) {
+        case 'init':
+            return startServer(data.defs, data.plugins, data.scripts)
+        case 'add':
+            return server.addFile(data.name, data.text)
+        case 'del':
+            return server.delFile(data.name)
+        case 'req':
+            return server.request(data.body, function (err, reqData) {
+                postMessage({ id: data.id, body: reqData, err: err && String(err) })
+            })
+        case 'getFile':
+            var c = pending[data.id]
+            delete pending[data.id]
+            return c(data.err, data.text)
+        default:
+            throw new Error('Unknown message type: ' + data.type)
+    }
+}
+
+var nextId = 0,
+    pending = {}
+function getFile(file, c) {
+    postMessage({ type: 'getFile', name: file, id: ++nextId })
+    pending[nextId] = c
+}
+
+function startServer(defs, plugins, scripts) {
+    if (scripts) importScripts.apply(null, scripts)
+
+    server = new tern.Server({
+        getFile: getFile,
+        async: true,
+        defs: defs,
+        plugins: plugins,
+    })
+}
+
+this.console = {
+    log: function (v) {
+        postMessage({ type: 'debug', message: v })
+    },
+}

+ 168 - 0
libs/codemirror/addon/wrap/hardwrap.js

@@ -0,0 +1,168 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    var Pos = CodeMirror.Pos
+
+    function findParagraph(cm, pos, options) {
+        var startRE = options.paragraphStart || cm.getHelper(pos, 'paragraphStart')
+        for (var start = pos.line, first = cm.firstLine(); start > first; --start) {
+            var line = cm.getLine(start)
+            if (startRE && startRE.test(line)) break
+            if (!/\S/.test(line)) {
+                ++start
+                break
+            }
+        }
+        var endRE = options.paragraphEnd || cm.getHelper(pos, 'paragraphEnd')
+        for (var end = pos.line + 1, last = cm.lastLine(); end <= last; ++end) {
+            var line = cm.getLine(end)
+            if (endRE && endRE.test(line)) {
+                ++end
+                break
+            }
+            if (!/\S/.test(line)) break
+        }
+        return { from: start, to: end }
+    }
+
+    function findBreakPoint(text, column, wrapOn, killTrailingSpace, forceBreak) {
+        var at = column
+        while (at < text.length && text.charAt(at) == ' ') at++
+        for (; at > 0; --at) if (wrapOn.test(text.slice(at - 1, at + 1))) break
+
+        if (!forceBreak && at <= text.match(/^[ \t]*/)[0].length) {
+            // didn't find a break point before column, in non-forceBreak mode try to
+            // find one after 'column'.
+            for (at = column + 1; at < text.length - 1; ++at) {
+                if (wrapOn.test(text.slice(at - 1, at + 1))) break
+            }
+        }
+
+        for (var first = true; ; first = false) {
+            var endOfText = at
+            if (killTrailingSpace) while (text.charAt(endOfText - 1) == ' ') --endOfText
+            if (endOfText == 0 && first) at = column
+            else return { from: endOfText, to: at }
+        }
+    }
+
+    function wrapRange(cm, from, to, options) {
+        from = cm.clipPos(from)
+        to = cm.clipPos(to)
+        var column = options.column || 80
+        var wrapOn = options.wrapOn || /\s\S|-[^\.\d]/
+        var forceBreak = options.forceBreak !== false
+        var killTrailing = options.killTrailingSpace !== false
+        var changes = [],
+            curLine = '',
+            curNo = from.line
+        var lines = cm.getRange(from, to, false)
+        if (!lines.length) return null
+        var leadingSpace = lines[0].match(/^[ \t]*/)[0]
+        if (leadingSpace.length >= column) column = leadingSpace.length + 1
+
+        for (var i = 0; i < lines.length; ++i) {
+            var text = lines[i],
+                oldLen = curLine.length,
+                spaceInserted = 0
+            if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) {
+                curLine += ' '
+                spaceInserted = 1
+            }
+            var spaceTrimmed = ''
+            if (i) {
+                spaceTrimmed = text.match(/^\s*/)[0]
+                text = text.slice(spaceTrimmed.length)
+            }
+            curLine += text
+            if (i) {
+                var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed && findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak)
+                // If this isn't broken, or is broken at a different point, remove old break
+                if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) {
+                    changes.push({ text: [spaceInserted ? ' ' : ''], from: Pos(curNo, oldLen), to: Pos(curNo + 1, spaceTrimmed.length) })
+                } else {
+                    curLine = leadingSpace + text
+                    ++curNo
+                }
+            }
+            while (curLine.length > column) {
+                var bp = findBreakPoint(curLine, column, wrapOn, killTrailing, forceBreak)
+                if (bp.from != bp.to || (forceBreak && leadingSpace !== curLine.slice(0, bp.to))) {
+                    changes.push({ text: ['', leadingSpace], from: Pos(curNo, bp.from), to: Pos(curNo, bp.to) })
+                    curLine = leadingSpace + curLine.slice(bp.to)
+                    ++curNo
+                } else {
+                    break
+                }
+            }
+        }
+        if (changes.length)
+            cm.operation(function () {
+                for (var i = 0; i < changes.length; ++i) {
+                    var change = changes[i]
+                    if (change.text || CodeMirror.cmpPos(change.from, change.to)) cm.replaceRange(change.text, change.from, change.to)
+                }
+            })
+        return changes.length ? { from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1]) } : null
+    }
+
+    CodeMirror.defineExtension('wrapParagraph', function (pos, options) {
+        options = options || {}
+        if (!pos) pos = this.getCursor()
+        var para = findParagraph(this, pos, options)
+        return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options)
+    })
+
+    CodeMirror.commands.wrapLines = function (cm) {
+        cm.operation(function () {
+            var ranges = cm.listSelections(),
+                at = cm.lastLine() + 1
+            for (var i = ranges.length - 1; i >= 0; i--) {
+                var range = ranges[i],
+                    span
+                if (range.empty()) {
+                    var para = findParagraph(cm, range.head, {})
+                    span = { from: Pos(para.from, 0), to: Pos(para.to - 1) }
+                } else {
+                    span = { from: range.from(), to: range.to() }
+                }
+                if (span.to.line >= at) continue
+                at = span.from.line
+                wrapRange(cm, span.from, span.to, {})
+            }
+        })
+    }
+
+    CodeMirror.defineExtension('wrapRange', function (from, to, options) {
+        return wrapRange(this, from, to, options || {})
+    })
+
+    CodeMirror.defineExtension('wrapParagraphsInRange', function (from, to, options) {
+        options = options || {}
+        var cm = this,
+            paras = []
+        for (var line = from.line; line <= to.line; ) {
+            var para = findParagraph(cm, Pos(line, 0), options)
+            paras.push(para)
+            line = para.to
+        }
+        var madeChange = false
+        if (paras.length)
+            cm.operation(function () {
+                for (var i = paras.length - 1; i >= 0; --i) madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options)
+            })
+        return madeChange
+    })
+})

+ 344 - 0
libs/codemirror/codemirror.css

@@ -0,0 +1,344 @@
+/* BASICS */
+
+.CodeMirror {
+  /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
+  height: 300px;
+  color: black;
+  direction: ltr;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+  padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre.CodeMirror-line,
+.CodeMirror pre.CodeMirror-line-like {
+  padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+  border-right: 1px solid #ddd;
+  background-color: #f7f7f7;
+  white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+  padding: 0 3px 0 5px;
+  min-width: 20px;
+  text-align: right;
+  color: #999;
+  white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror-cursor {
+  border-left: 1px solid black;
+  border-right: none;
+  width: 0;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+  border-left: 1px solid silver;
+}
+.cm-fat-cursor .CodeMirror-cursor {
+  width: auto;
+  border: 0 !important;
+  background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+  z-index: 1;
+}
+.cm-fat-cursor .CodeMirror-line::selection,
+.cm-fat-cursor .CodeMirror-line > span::selection, 
+.cm-fat-cursor .CodeMirror-line > span > span::selection { background: transparent; }
+.cm-fat-cursor .CodeMirror-line::-moz-selection,
+.cm-fat-cursor .CodeMirror-line > span::-moz-selection,
+.cm-fat-cursor .CodeMirror-line > span > span::-moz-selection { background: transparent; }
+.cm-fat-cursor { caret-color: transparent; }
+@-moz-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@-webkit-keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+@keyframes blink {
+  0% {}
+  50% { background-color: transparent; }
+  100% {}
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror-overwrite .CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-rulers {
+  position: absolute;
+  left: 0; right: 0; top: -50px; bottom: 0;
+  overflow: hidden;
+}
+.CodeMirror-ruler {
+  border-left: 1px solid #ccc;
+  top: 0; bottom: 0;
+  position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+  position: relative;
+  overflow: hidden;
+  background: white;
+}
+
+.CodeMirror-scroll {
+  overflow: scroll !important; /* Things will break if this is overridden */
+  /* 50px is the magic margin used to hide the element's real scrollbars */
+  /* See overflow: hidden in .CodeMirror */
+  margin-bottom: -50px; margin-right: -50px;
+  padding-bottom: 50px;
+  height: 100%;
+  outline: none; /* Prevent dragging from highlighting the element */
+  position: relative;
+  z-index: 0;
+}
+.CodeMirror-sizer {
+  position: relative;
+  border-right: 50px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actual scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  position: absolute;
+  z-index: 6;
+  display: none;
+  outline: none;
+}
+.CodeMirror-vscrollbar {
+  right: 0; top: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+  bottom: 0; left: 0;
+  overflow-y: hidden;
+  overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+  right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+  left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+  position: absolute; left: 0; top: 0;
+  min-height: 100%;
+  z-index: 3;
+}
+.CodeMirror-gutter {
+  white-space: normal;
+  height: 100%;
+  display: inline-block;
+  vertical-align: top;
+  margin-bottom: -50px;
+}
+.CodeMirror-gutter-wrapper {
+  position: absolute;
+  z-index: 4;
+  background: none !important;
+  border: none !important;
+}
+.CodeMirror-gutter-background {
+  position: absolute;
+  top: 0; bottom: 0;
+  z-index: 4;
+}
+.CodeMirror-gutter-elt {
+  position: absolute;
+  cursor: default;
+  z-index: 4;
+}
+.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
+.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
+
+.CodeMirror-lines {
+  cursor: text;
+  min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre.CodeMirror-line,
+.CodeMirror pre.CodeMirror-line-like {
+  /* Reset some styles that the rest of the page might have set */
+  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+  border-width: 0;
+  background: transparent;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  white-space: pre;
+  word-wrap: normal;
+  line-height: inherit;
+  color: inherit;
+  z-index: 2;
+  position: relative;
+  overflow: visible;
+  -webkit-tap-highlight-color: transparent;
+  -webkit-font-variant-ligatures: contextual;
+  font-variant-ligatures: contextual;
+}
+.CodeMirror-wrap pre.CodeMirror-line,
+.CodeMirror-wrap pre.CodeMirror-line-like {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  word-break: normal;
+}
+
+.CodeMirror-linebackground {
+  position: absolute;
+  left: 0; right: 0; top: 0; bottom: 0;
+  z-index: 0;
+}
+
+.CodeMirror-linewidget {
+  position: relative;
+  z-index: 2;
+  padding: 0.1px; /* Force widget margins to stay inside of the container */
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-rtl pre { direction: rtl; }
+
+.CodeMirror-code {
+  outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+  position: absolute;
+  width: 100%;
+  height: 0;
+  overflow: hidden;
+  visibility: hidden;
+}
+
+.CodeMirror-cursor {
+  position: absolute;
+  pointer-events: none;
+}
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+  visibility: hidden;
+  position: relative;
+  z-index: 3;
+}
+div.CodeMirror-dragcursors {
+  visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+  visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+  background-color: #ffa;
+  background-color: rgba(255, 255, 0, .4);
+}
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+  /* Hide the cursor when printing */
+  .CodeMirror div.CodeMirror-cursors {
+    visibility: hidden;
+  }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }

Dosya farkı çok büyük olduğundan ihmal edildi
+ 13058 - 0
libs/codemirror/codemirror.js


+ 175 - 0
libs/codemirror/mode/apl/apl.js

@@ -0,0 +1,175 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('apl', function () {
+        var builtInOps = {
+            '.': 'innerProduct',
+            '\\': 'scan',
+            '/': 'reduce',
+            '⌿': 'reduce1Axis',
+            '⍀': 'scan1Axis',
+            '¨': 'each',
+            '⍣': 'power',
+        }
+        var builtInFuncs = {
+            '+': ['conjugate', 'add'],
+            '−': ['negate', 'subtract'],
+            '×': ['signOf', 'multiply'],
+            '÷': ['reciprocal', 'divide'],
+            '⌈': ['ceiling', 'greaterOf'],
+            '⌊': ['floor', 'lesserOf'],
+            '∣': ['absolute', 'residue'],
+            '⍳': ['indexGenerate', 'indexOf'],
+            '?': ['roll', 'deal'],
+            '⋆': ['exponentiate', 'toThePowerOf'],
+            '⍟': ['naturalLog', 'logToTheBase'],
+            '○': ['piTimes', 'circularFuncs'],
+            '!': ['factorial', 'binomial'],
+            '⌹': ['matrixInverse', 'matrixDivide'],
+            '<': [null, 'lessThan'],
+            '≤': [null, 'lessThanOrEqual'],
+            '=': [null, 'equals'],
+            '>': [null, 'greaterThan'],
+            '≥': [null, 'greaterThanOrEqual'],
+            '≠': [null, 'notEqual'],
+            '≡': ['depth', 'match'],
+            '≢': [null, 'notMatch'],
+            '∈': ['enlist', 'membership'],
+            '⍷': [null, 'find'],
+            '∪': ['unique', 'union'],
+            '∩': [null, 'intersection'],
+            '∼': ['not', 'without'],
+            '∨': [null, 'or'],
+            '∧': [null, 'and'],
+            '⍱': [null, 'nor'],
+            '⍲': [null, 'nand'],
+            '⍴': ['shapeOf', 'reshape'],
+            ',': ['ravel', 'catenate'],
+            '⍪': [null, 'firstAxisCatenate'],
+            '⌽': ['reverse', 'rotate'],
+            '⊖': ['axis1Reverse', 'axis1Rotate'],
+            '⍉': ['transpose', null],
+            '↑': ['first', 'take'],
+            '↓': [null, 'drop'],
+            '⊂': ['enclose', 'partitionWithAxis'],
+            '⊃': ['diclose', 'pick'],
+            '⌷': [null, 'index'],
+            '⍋': ['gradeUp', null],
+            '⍒': ['gradeDown', null],
+            '⊤': ['encode', null],
+            '⊥': ['decode', null],
+            '⍕': ['format', 'formatByExample'],
+            '⍎': ['execute', null],
+            '⊣': ['stop', 'left'],
+            '⊢': ['pass', 'right'],
+        }
+
+        var isOperator = /[\.\/⌿⍀¨⍣]/
+        var isNiladic = /⍬/
+        var isFunction = /[\+−×÷⌈⌊∣⍳\?⋆⍟○!⌹<≤=>≥≠≡≢∈⍷∪∩∼∨∧⍱⍲⍴,⍪⌽⊖⍉↑↓⊂⊃⌷⍋⍒⊤⊥⍕⍎⊣⊢]/
+        var isArrow = /←/
+        var isComment = /[⍝#].*$/
+
+        var stringEater = function (type) {
+            var prev
+            prev = false
+            return function (c) {
+                prev = c
+                if (c === type) {
+                    return prev === '\\'
+                }
+                return true
+            }
+        }
+        return {
+            startState: function () {
+                return {
+                    prev: false,
+                    func: false,
+                    op: false,
+                    string: false,
+                    escape: false,
+                }
+            },
+            token: function (stream, state) {
+                var ch, funcName
+                if (stream.eatSpace()) {
+                    return null
+                }
+                ch = stream.next()
+                if (ch === '"' || ch === "'") {
+                    stream.eatWhile(stringEater(ch))
+                    stream.next()
+                    state.prev = true
+                    return 'string'
+                }
+                if (/[\[{\(]/.test(ch)) {
+                    state.prev = false
+                    return null
+                }
+                if (/[\]}\)]/.test(ch)) {
+                    state.prev = true
+                    return null
+                }
+                if (isNiladic.test(ch)) {
+                    state.prev = false
+                    return 'niladic'
+                }
+                if (/[¯\d]/.test(ch)) {
+                    if (state.func) {
+                        state.func = false
+                        state.prev = false
+                    } else {
+                        state.prev = true
+                    }
+                    stream.eatWhile(/[\w\.]/)
+                    return 'number'
+                }
+                if (isOperator.test(ch)) {
+                    return 'operator apl-' + builtInOps[ch]
+                }
+                if (isArrow.test(ch)) {
+                    return 'apl-arrow'
+                }
+                if (isFunction.test(ch)) {
+                    funcName = 'apl-'
+                    if (builtInFuncs[ch] != null) {
+                        if (state.prev) {
+                            funcName += builtInFuncs[ch][1]
+                        } else {
+                            funcName += builtInFuncs[ch][0]
+                        }
+                    }
+                    state.func = true
+                    state.prev = false
+                    return 'function ' + funcName
+                }
+                if (isComment.test(ch)) {
+                    stream.skipToEnd()
+                    return 'comment'
+                }
+                if (ch === '∘' && stream.peek() === '.') {
+                    stream.next()
+                    return 'function jot-dot'
+                }
+                stream.eatWhile(/[\w\$_]/)
+                state.prev = true
+                return 'keyword'
+            },
+        }
+    })
+
+    CodeMirror.defineMIME('text/apl', 'apl')
+})

+ 72 - 0
libs/codemirror/mode/apl/index.html

@@ -0,0 +1,72 @@
+<!doctype html>
+
+<title>CodeMirror: APL mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<script src="./apl.js"></script>
+<style>
+	.CodeMirror { border: 2px inset #dee; }
+    </style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">APL</a>
+  </ul>
+</div>
+
+<article>
+<h2>APL mode</h2>
+<form><textarea id="code" name="code">
+⍝ Conway's game of life
+
+⍝ This example was inspired by the impressive demo at
+⍝ http://www.youtube.com/watch?v=a9xAKttWgP4
+
+⍝ Create a matrix:
+⍝     0 1 1
+⍝     1 1 0
+⍝     0 1 0
+creature ← (3 3 ⍴ ⍳ 9) ∈ 1 2 3 4 7   ⍝ Original creature from demo
+creature ← (3 3 ⍴ ⍳ 9) ∈ 1 3 6 7 8   ⍝ Glider
+
+⍝ Place the creature on a larger board, near the centre
+board ← ¯1 ⊖ ¯2 ⌽ 5 7 ↑ creature
+
+⍝ A function to move from one generation to the next
+life ← {∨/ 1 ⍵ ∧ 3 4 = ⊂+/ +⌿ 1 0 ¯1 ∘.⊖ 1 0 ¯1 ⌽¨ ⊂⍵}
+
+⍝ Compute n-th generation and format it as a
+⍝ character matrix
+gen ← {' #'[(life ⍣ ⍵) board]}
+
+⍝ Show first three generations
+(gen 1) (gen 2) (gen 3)
+</textarea></form>
+
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        mode: "text/apl"
+      });
+    </script>
+
+    <p>Simple mode that tries to handle APL as well as it can.</p>
+    <p>It attempts to label functions/operators based upon
+    monadic/dyadic usage (but this is far from fully fleshed out).
+    This means there are meaningful classnames so hover states can
+    have popups etc.</p>
+
+    <p><strong>MIME types defined:</strong> <code>text/apl</code> (APL code)</p>
+  </article>

+ 76 - 0
libs/codemirror/mode/asciiarmor/asciiarmor.js

@@ -0,0 +1,76 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    function errorIfNotEmpty(stream) {
+        var nonWS = stream.match(/^\s*\S/)
+        stream.skipToEnd()
+        return nonWS ? 'error' : null
+    }
+
+    CodeMirror.defineMode('asciiarmor', function () {
+        return {
+            token: function (stream, state) {
+                var m
+                if (state.state == 'top') {
+                    if (stream.sol() && (m = stream.match(/^-----BEGIN (.*)?-----\s*$/))) {
+                        state.state = 'headers'
+                        state.type = m[1]
+                        return 'tag'
+                    }
+                    return errorIfNotEmpty(stream)
+                } else if (state.state == 'headers') {
+                    if (stream.sol() && stream.match(/^\w+:/)) {
+                        state.state = 'header'
+                        return 'atom'
+                    } else {
+                        var result = errorIfNotEmpty(stream)
+                        if (result) state.state = 'body'
+                        return result
+                    }
+                } else if (state.state == 'header') {
+                    stream.skipToEnd()
+                    state.state = 'headers'
+                    return 'string'
+                } else if (state.state == 'body') {
+                    if (stream.sol() && (m = stream.match(/^-----END (.*)?-----\s*$/))) {
+                        if (m[1] != state.type) return 'error'
+                        state.state = 'end'
+                        return 'tag'
+                    } else {
+                        if (stream.eatWhile(/[A-Za-z0-9+\/=]/)) {
+                            return null
+                        } else {
+                            stream.next()
+                            return 'error'
+                        }
+                    }
+                } else if (state.state == 'end') {
+                    return errorIfNotEmpty(stream)
+                }
+            },
+            blankLine: function (state) {
+                if (state.state == 'headers') state.state = 'body'
+            },
+            startState: function () {
+                return { state: 'top', type: null }
+            },
+        }
+    })
+
+    CodeMirror.defineMIME('application/pgp', 'asciiarmor')
+    CodeMirror.defineMIME('application/pgp-encrypted', 'asciiarmor')
+    CodeMirror.defineMIME('application/pgp-keys', 'asciiarmor')
+    CodeMirror.defineMIME('application/pgp-signature', 'asciiarmor')
+})

+ 46 - 0
libs/codemirror/mode/asciiarmor/index.html

@@ -0,0 +1,46 @@
+<!doctype html>
+
+<title>CodeMirror: ASCII Armor (PGP) mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="asciiarmor.js"></script>
+<style>.CodeMirror {background: #f8f8f8;}</style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">ASCII Armor</a>
+  </ul>
+</div>
+
+<article>
+<h2>ASCII Armor (PGP) mode</h2>
+<form><textarea id="code" name="code">
+-----BEGIN PGP MESSAGE-----
+Version: OpenPrivacy 0.99
+
+yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS
+vBSFjNSiVHsuAA==
+=njUN
+-----END PGP MESSAGE-----
+</textarea></form>
+
+<script>
+var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+  lineNumbers: true
+});
+</script>
+
+<p><strong>MIME types
+defined:</strong> <code>application/pgp</code>, <code>application/pgp-encrypted</code>, <code>application/pgp-keys</code>, <code>application/pgp-signature</code></p>
+
+</article>

+ 203 - 0
libs/codemirror/mode/asn.1/asn.1.js

@@ -0,0 +1,203 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('asn.1', function (config, parserConfig) {
+        var indentUnit = config.indentUnit,
+            keywords = parserConfig.keywords || {},
+            cmipVerbs = parserConfig.cmipVerbs || {},
+            compareTypes = parserConfig.compareTypes || {},
+            status = parserConfig.status || {},
+            tags = parserConfig.tags || {},
+            storage = parserConfig.storage || {},
+            modifier = parserConfig.modifier || {},
+            accessTypes = parserConfig.accessTypes || {},
+            multiLineStrings = parserConfig.multiLineStrings,
+            indentStatements = parserConfig.indentStatements !== false
+        var isOperatorChar = /[\|\^]/
+        var curPunc
+
+        function tokenBase(stream, state) {
+            var ch = stream.next()
+            if (ch == '"' || ch == "'") {
+                state.tokenize = tokenString(ch)
+                return state.tokenize(stream, state)
+            }
+            if (/[\[\]\(\){}:=,;]/.test(ch)) {
+                curPunc = ch
+                return 'punctuation'
+            }
+            if (ch == '-') {
+                if (stream.eat('-')) {
+                    stream.skipToEnd()
+                    return 'comment'
+                }
+            }
+            if (/\d/.test(ch)) {
+                stream.eatWhile(/[\w\.]/)
+                return 'number'
+            }
+            if (isOperatorChar.test(ch)) {
+                stream.eatWhile(isOperatorChar)
+                return 'operator'
+            }
+
+            stream.eatWhile(/[\w\-]/)
+            var cur = stream.current()
+            if (keywords.propertyIsEnumerable(cur)) return 'keyword'
+            if (cmipVerbs.propertyIsEnumerable(cur)) return 'variable cmipVerbs'
+            if (compareTypes.propertyIsEnumerable(cur)) return 'atom compareTypes'
+            if (status.propertyIsEnumerable(cur)) return 'comment status'
+            if (tags.propertyIsEnumerable(cur)) return 'variable-3 tags'
+            if (storage.propertyIsEnumerable(cur)) return 'builtin storage'
+            if (modifier.propertyIsEnumerable(cur)) return 'string-2 modifier'
+            if (accessTypes.propertyIsEnumerable(cur)) return 'atom accessTypes'
+
+            return 'variable'
+        }
+
+        function tokenString(quote) {
+            return function (stream, state) {
+                var escaped = false,
+                    next,
+                    end = false
+                while ((next = stream.next()) != null) {
+                    if (next == quote && !escaped) {
+                        var afterNext = stream.peek()
+                        //look if the character if the quote is like the B in '10100010'B
+                        if (afterNext) {
+                            afterNext = afterNext.toLowerCase()
+                            if (afterNext == 'b' || afterNext == 'h' || afterNext == 'o') stream.next()
+                        }
+                        end = true
+                        break
+                    }
+                    escaped = !escaped && next == '\\'
+                }
+                if (end || !(escaped || multiLineStrings)) state.tokenize = null
+                return 'string'
+            }
+        }
+
+        function Context(indented, column, type, align, prev) {
+            this.indented = indented
+            this.column = column
+            this.type = type
+            this.align = align
+            this.prev = prev
+        }
+        function pushContext(state, col, type) {
+            var indent = state.indented
+            if (state.context && state.context.type == 'statement') indent = state.context.indented
+            return (state.context = new Context(indent, col, type, null, state.context))
+        }
+        function popContext(state) {
+            var t = state.context.type
+            if (t == ')' || t == ']' || t == '}') state.indented = state.context.indented
+            return (state.context = state.context.prev)
+        }
+
+        //Interface
+        return {
+            startState: function (basecolumn) {
+                return {
+                    tokenize: null,
+                    context: new Context((basecolumn || 0) - indentUnit, 0, 'top', false),
+                    indented: 0,
+                    startOfLine: true,
+                }
+            },
+
+            token: function (stream, state) {
+                var ctx = state.context
+                if (stream.sol()) {
+                    if (ctx.align == null) ctx.align = false
+                    state.indented = stream.indentation()
+                    state.startOfLine = true
+                }
+                if (stream.eatSpace()) return null
+                curPunc = null
+                var style = (state.tokenize || tokenBase)(stream, state)
+                if (style == 'comment') return style
+                if (ctx.align == null) ctx.align = true
+
+                if ((curPunc == ';' || curPunc == ':' || curPunc == ',') && ctx.type == 'statement') {
+                    popContext(state)
+                } else if (curPunc == '{') pushContext(state, stream.column(), '}')
+                else if (curPunc == '[') pushContext(state, stream.column(), ']')
+                else if (curPunc == '(') pushContext(state, stream.column(), ')')
+                else if (curPunc == '}') {
+                    while (ctx.type == 'statement') ctx = popContext(state)
+                    if (ctx.type == '}') ctx = popContext(state)
+                    while (ctx.type == 'statement') ctx = popContext(state)
+                } else if (curPunc == ctx.type) popContext(state)
+                else if (indentStatements && (((ctx.type == '}' || ctx.type == 'top') && curPunc != ';') || (ctx.type == 'statement' && curPunc == 'newstatement')))
+                    pushContext(state, stream.column(), 'statement')
+
+                state.startOfLine = false
+                return style
+            },
+
+            electricChars: '{}',
+            lineComment: '--',
+            fold: 'brace',
+        }
+    })
+
+    function words(str) {
+        var obj = {},
+            words = str.split(' ')
+        for (var i = 0; i < words.length; ++i) obj[words[i]] = true
+        return obj
+    }
+
+    CodeMirror.defineMIME('text/x-ttcn-asn', {
+        name: 'asn.1',
+        keywords: words(
+            'DEFINITIONS OBJECTS IF DERIVED INFORMATION ACTION' +
+                ' REPLY ANY NAMED CHARACTERIZED BEHAVIOUR REGISTERED' +
+                ' WITH AS IDENTIFIED CONSTRAINED BY PRESENT BEGIN' +
+                ' IMPORTS FROM UNITS SYNTAX MIN-ACCESS MAX-ACCESS' +
+                ' MINACCESS MAXACCESS REVISION STATUS DESCRIPTION' +
+                ' SEQUENCE SET COMPONENTS OF CHOICE DistinguishedName' +
+                ' ENUMERATED SIZE MODULE END INDEX AUGMENTS EXTENSIBILITY' +
+                ' IMPLIED EXPORTS'
+        ),
+        cmipVerbs: words('ACTIONS ADD GET NOTIFICATIONS REPLACE REMOVE'),
+        compareTypes: words(
+            'OPTIONAL DEFAULT MANAGED MODULE-TYPE MODULE_IDENTITY' +
+                ' MODULE-COMPLIANCE OBJECT-TYPE OBJECT-IDENTITY' +
+                ' OBJECT-COMPLIANCE MODE CONFIRMED CONDITIONAL' +
+                ' SUBORDINATE SUPERIOR CLASS TRUE FALSE NULL' +
+                ' TEXTUAL-CONVENTION'
+        ),
+        status: words('current deprecated mandatory obsolete'),
+        tags: words('APPLICATION AUTOMATIC EXPLICIT IMPLICIT PRIVATE TAGS' + ' UNIVERSAL'),
+        storage: words(
+            'BOOLEAN INTEGER OBJECT IDENTIFIER BIT OCTET STRING' +
+                ' UTCTime InterfaceIndex IANAifType CMIP-Attribute' +
+                ' REAL PACKAGE PACKAGES IpAddress PhysAddress' +
+                ' NetworkAddress BITS BMPString TimeStamp TimeTicks' +
+                ' TruthValue RowStatus DisplayString GeneralString' +
+                ' GraphicString IA5String NumericString' +
+                ' PrintableString SnmpAdminString TeletexString' +
+                ' UTF8String VideotexString VisibleString StringStore' +
+                ' ISO646String T61String UniversalString Unsigned32' +
+                ' Integer32 Gauge Gauge32 Counter Counter32 Counter64'
+        ),
+        modifier: words('ATTRIBUTE ATTRIBUTES MANDATORY-GROUP MANDATORY-GROUPS' + ' GROUP GROUPS ELEMENTS EQUALITY ORDERING SUBSTRINGS' + ' DEFINED'),
+        accessTypes: words('not-accessible accessible-for-notify read-only' + ' read-create read-write'),
+        multiLineStrings: true,
+    })
+})

+ 78 - 0
libs/codemirror/mode/asn.1/index.html

@@ -0,0 +1,78 @@
+<!doctype html>
+
+<title>CodeMirror: ASN.1 mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<script src="asn.1.js"></script>
+<style>
+    .CodeMirror {
+        border-top: 1px solid black;
+        border-bottom: 1px solid black;
+    }
+</style>
+<div id=nav>
+    <a href="https://codemirror.net"><h1>CodeMirror</h1>
+        <img id=logo src="../../doc/logo.png" alt="">
+    </a>
+
+    <ul>
+        <li><a href="../../index.html">Home</a>
+        <li><a href="../../doc/manual.html">Manual</a>
+        <li><a href="https://github.com/codemirror/codemirror">Code</a>
+    </ul>
+    <ul>
+        <li><a href="../index.html">Language modes</a>
+        <li><a class=active href="http://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One">ASN.1</a>
+    </ul>
+</div>
+<article>
+    <h2>ASN.1 example</h2>
+    <div>
+        <textarea id="ttcn-asn-code">
+ --
+ -- Sample ASN.1 Code
+ --
+ MyModule DEFINITIONS ::=
+ BEGIN
+
+ MyTypes ::= SEQUENCE {
+     myObjectId   OBJECT IDENTIFIER,
+     mySeqOf      SEQUENCE OF MyInt,
+     myBitString  BIT STRING {
+                         muxToken(0),
+                         modemToken(1)
+                  }
+ }
+
+ MyInt ::= INTEGER (0..65535)
+
+ END
+        </textarea>
+    </div>
+
+    <script>
+        var ttcnEditor = CodeMirror.fromTextArea(document.getElementById("ttcn-asn-code"), {
+            lineNumbers: true,
+            matchBrackets: true,
+            mode: "text/x-ttcn-asn"
+        });
+        ttcnEditor.setSize(400, 400);
+        var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;
+        CodeMirror.keyMap.default[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
+    </script>
+    <br/>
+    <p><strong>Language:</strong> Abstract Syntax Notation One
+        (<a href="http://www.itu.int/en/ITU-T/asn1/Pages/introduction.aspx">ASN.1</a>)
+    </p>
+    <p><strong>MIME types defined:</strong> <code>text/x-ttcn-asn</code></p>
+
+    <br/>
+    <p>The development of this mode has been sponsored by <a href="http://www.ericsson.com/">Ericsson
+    </a>.</p>
+    <p>Coded by Asmelash Tsegay Gebretsadkan </p>
+</article>
+

+ 394 - 0
libs/codemirror/mode/asterisk/asterisk.js

@@ -0,0 +1,394 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/*
+ * =====================================================================================
+ *
+ *       Filename:  mode/asterisk/asterisk.js
+ *
+ *    Description:  CodeMirror mode for Asterisk dialplan
+ *
+ *        Created:  05/17/2012 09:20:25 PM
+ *       Revision:  08/05/2019 AstLinux Project: Support block-comments
+ *
+ *         Author:  Stas Kobzar (stas@modulis.ca),
+ *        Company:  Modulis.ca Inc.
+ *
+ * =====================================================================================
+ */
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('asterisk', function () {
+        var atoms = ['exten', 'same', 'include', 'ignorepat', 'switch'],
+            dpcmd = ['#include', '#exec'],
+            apps = [
+                'addqueuemember',
+                'adsiprog',
+                'aelsub',
+                'agentlogin',
+                'agentmonitoroutgoing',
+                'agi',
+                'alarmreceiver',
+                'amd',
+                'answer',
+                'authenticate',
+                'background',
+                'backgrounddetect',
+                'bridge',
+                'busy',
+                'callcompletioncancel',
+                'callcompletionrequest',
+                'celgenuserevent',
+                'changemonitor',
+                'chanisavail',
+                'channelredirect',
+                'chanspy',
+                'clearhash',
+                'confbridge',
+                'congestion',
+                'continuewhile',
+                'controlplayback',
+                'dahdiacceptr2call',
+                'dahdibarge',
+                'dahdiras',
+                'dahdiscan',
+                'dahdisendcallreroutingfacility',
+                'dahdisendkeypadfacility',
+                'datetime',
+                'dbdel',
+                'dbdeltree',
+                'deadagi',
+                'dial',
+                'dictate',
+                'directory',
+                'disa',
+                'dumpchan',
+                'eagi',
+                'echo',
+                'endwhile',
+                'exec',
+                'execif',
+                'execiftime',
+                'exitwhile',
+                'extenspy',
+                'externalivr',
+                'festival',
+                'flash',
+                'followme',
+                'forkcdr',
+                'getcpeid',
+                'gosub',
+                'gosubif',
+                'goto',
+                'gotoif',
+                'gotoiftime',
+                'hangup',
+                'iax2provision',
+                'ices',
+                'importvar',
+                'incomplete',
+                'ivrdemo',
+                'jabberjoin',
+                'jabberleave',
+                'jabbersend',
+                'jabbersendgroup',
+                'jabberstatus',
+                'jack',
+                'log',
+                'macro',
+                'macroexclusive',
+                'macroexit',
+                'macroif',
+                'mailboxexists',
+                'meetme',
+                'meetmeadmin',
+                'meetmechanneladmin',
+                'meetmecount',
+                'milliwatt',
+                'minivmaccmess',
+                'minivmdelete',
+                'minivmgreet',
+                'minivmmwi',
+                'minivmnotify',
+                'minivmrecord',
+                'mixmonitor',
+                'monitor',
+                'morsecode',
+                'mp3player',
+                'mset',
+                'musiconhold',
+                'nbscat',
+                'nocdr',
+                'noop',
+                'odbc',
+                'odbc',
+                'odbcfinish',
+                'originate',
+                'ospauth',
+                'ospfinish',
+                'osplookup',
+                'ospnext',
+                'page',
+                'park',
+                'parkandannounce',
+                'parkedcall',
+                'pausemonitor',
+                'pausequeuemember',
+                'pickup',
+                'pickupchan',
+                'playback',
+                'playtones',
+                'privacymanager',
+                'proceeding',
+                'progress',
+                'queue',
+                'queuelog',
+                'raiseexception',
+                'read',
+                'readexten',
+                'readfile',
+                'receivefax',
+                'receivefax',
+                'receivefax',
+                'record',
+                'removequeuemember',
+                'resetcdr',
+                'retrydial',
+                'return',
+                'ringing',
+                'sayalpha',
+                'saycountedadj',
+                'saycountednoun',
+                'saycountpl',
+                'saydigits',
+                'saynumber',
+                'sayphonetic',
+                'sayunixtime',
+                'senddtmf',
+                'sendfax',
+                'sendfax',
+                'sendfax',
+                'sendimage',
+                'sendtext',
+                'sendurl',
+                'set',
+                'setamaflags',
+                'setcallerpres',
+                'setmusiconhold',
+                'sipaddheader',
+                'sipdtmfmode',
+                'sipremoveheader',
+                'skel',
+                'slastation',
+                'slatrunk',
+                'sms',
+                'softhangup',
+                'speechactivategrammar',
+                'speechbackground',
+                'speechcreate',
+                'speechdeactivategrammar',
+                'speechdestroy',
+                'speechloadgrammar',
+                'speechprocessingsound',
+                'speechstart',
+                'speechunloadgrammar',
+                'stackpop',
+                'startmusiconhold',
+                'stopmixmonitor',
+                'stopmonitor',
+                'stopmusiconhold',
+                'stopplaytones',
+                'system',
+                'testclient',
+                'testserver',
+                'transfer',
+                'tryexec',
+                'trysystem',
+                'unpausemonitor',
+                'unpausequeuemember',
+                'userevent',
+                'verbose',
+                'vmauthenticate',
+                'vmsayname',
+                'voicemail',
+                'voicemailmain',
+                'wait',
+                'waitexten',
+                'waitfornoise',
+                'waitforring',
+                'waitforsilence',
+                'waitmusiconhold',
+                'waituntil',
+                'while',
+                'zapateller',
+            ]
+
+        function basicToken(stream, state) {
+            var cur = ''
+            var ch = stream.next()
+            // comment
+            if (state.blockComment) {
+                if (ch == '-' && stream.match('-;', true)) {
+                    state.blockComment = false
+                } else if (stream.skipTo('--;')) {
+                    stream.next()
+                    stream.next()
+                    stream.next()
+                    state.blockComment = false
+                } else {
+                    stream.skipToEnd()
+                }
+                return 'comment'
+            }
+            if (ch == ';') {
+                if (stream.match('--', true)) {
+                    if (!stream.match('-', false)) {
+                        // Except ;--- is not a block comment
+                        state.blockComment = true
+                        return 'comment'
+                    }
+                }
+                stream.skipToEnd()
+                return 'comment'
+            }
+            // context
+            if (ch == '[') {
+                stream.skipTo(']')
+                stream.eat(']')
+                return 'header'
+            }
+            // string
+            if (ch == '"') {
+                stream.skipTo('"')
+                return 'string'
+            }
+            if (ch == "'") {
+                stream.skipTo("'")
+                return 'string-2'
+            }
+            // dialplan commands
+            if (ch == '#') {
+                stream.eatWhile(/\w/)
+                cur = stream.current()
+                if (dpcmd.indexOf(cur) !== -1) {
+                    stream.skipToEnd()
+                    return 'strong'
+                }
+            }
+            // application args
+            if (ch == '$') {
+                var ch1 = stream.peek()
+                if (ch1 == '{') {
+                    stream.skipTo('}')
+                    stream.eat('}')
+                    return 'variable-3'
+                }
+            }
+            // extension
+            stream.eatWhile(/\w/)
+            cur = stream.current()
+            if (atoms.indexOf(cur) !== -1) {
+                state.extenStart = true
+                switch (cur) {
+                    case 'same':
+                        state.extenSame = true
+                        break
+                    case 'include':
+                    case 'switch':
+                    case 'ignorepat':
+                        state.extenInclude = true
+                        break
+                    default:
+                        break
+                }
+                return 'atom'
+            }
+        }
+
+        return {
+            startState: function () {
+                return {
+                    blockComment: false,
+                    extenStart: false,
+                    extenSame: false,
+                    extenInclude: false,
+                    extenExten: false,
+                    extenPriority: false,
+                    extenApplication: false,
+                }
+            },
+            token: function (stream, state) {
+                var cur = ''
+                if (stream.eatSpace()) return null
+                // extension started
+                if (state.extenStart) {
+                    stream.eatWhile(/[^\s]/)
+                    cur = stream.current()
+                    if (/^=>?$/.test(cur)) {
+                        state.extenExten = true
+                        state.extenStart = false
+                        return 'strong'
+                    } else {
+                        state.extenStart = false
+                        stream.skipToEnd()
+                        return 'error'
+                    }
+                } else if (state.extenExten) {
+                    // set exten and priority
+                    state.extenExten = false
+                    state.extenPriority = true
+                    stream.eatWhile(/[^,]/)
+                    if (state.extenInclude) {
+                        stream.skipToEnd()
+                        state.extenPriority = false
+                        state.extenInclude = false
+                    }
+                    if (state.extenSame) {
+                        state.extenPriority = false
+                        state.extenSame = false
+                        state.extenApplication = true
+                    }
+                    return 'tag'
+                } else if (state.extenPriority) {
+                    state.extenPriority = false
+                    state.extenApplication = true
+                    stream.next() // get comma
+                    if (state.extenSame) return null
+                    stream.eatWhile(/[^,]/)
+                    return 'number'
+                } else if (state.extenApplication) {
+                    stream.eatWhile(/,/)
+                    cur = stream.current()
+                    if (cur === ',') return null
+                    stream.eatWhile(/\w/)
+                    cur = stream.current().toLowerCase()
+                    state.extenApplication = false
+                    if (apps.indexOf(cur) !== -1) {
+                        return 'def strong'
+                    }
+                } else {
+                    return basicToken(stream, state)
+                }
+
+                return null
+            },
+
+            blockCommentStart: ';--',
+            blockCommentEnd: '--;',
+            lineComment: ';',
+        }
+    })
+
+    CodeMirror.defineMIME('text/x-asterisk', 'asterisk')
+})

+ 155 - 0
libs/codemirror/mode/asterisk/index.html

@@ -0,0 +1,155 @@
+<!doctype html>
+
+<title>CodeMirror: Asterisk dialplan mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<script src="asterisk.js"></script>
+<style>
+      .CodeMirror {border: 1px solid #999;}
+      .cm-s-default span.cm-arrow { color: red; }
+    </style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">Asterisk dialplan</a>
+  </ul>
+</div>
+
+<article>
+<h2>Asterisk dialplan mode</h2>
+<form><textarea id="code" name="code">
+; extensions.conf - the Asterisk dial plan
+;
+
+[general]
+;
+; If static is set to no, or omitted, then the pbx_config will rewrite
+; this file when extensions are modified.  Remember that all comments
+; made in the file will be lost when that happens.
+static=yes
+
+#include "/etc/asterisk/additional_general.conf
+
+[iaxprovider]
+switch => IAX2/user:[key]@myserver/mycontext
+
+[dynamic]
+#exec /usr/bin/dynamic-peers.pl
+
+[trunkint]
+;
+; International long distance through trunk
+;
+exten => _9011.,1,Macro(dundi-e164,${EXTEN:4})
+exten => _9011.,n,Dial(${GLOBAL(TRUNK)}/${FILTER(0-9,${EXTEN:${GLOBAL(TRUNKMSD)}})})
+
+[local]
+;
+; Master context for local, toll-free, and iaxtel calls only
+;
+ignorepat => 9
+include => default
+
+[demo]
+include => stdexten
+;
+; We start with what to do when a call first comes in.
+;
+exten => s,1,Wait(1)			; Wait a second, just for fun
+same  => n,Answer			; Answer the line
+same  => n,Set(TIMEOUT(digit)=5)	; Set Digit Timeout to 5 seconds
+same  => n,Set(TIMEOUT(response)=10)	; Set Response Timeout to 10 seconds
+same  => n(restart),BackGround(demo-congrats)	; Play a congratulatory message
+same  => n(instruct),BackGround(demo-instruct)	; Play some instructions
+same  => n,WaitExten			; Wait for an extension to be dialed.
+
+exten => 2,1,BackGround(demo-moreinfo)	; Give some more information.
+exten => 2,n,Goto(s,instruct)
+
+exten => 3,1,Set(LANGUAGE()=fr)		; Set language to french
+exten => 3,n,Goto(s,restart)		; Start with the congratulations
+
+exten => 1000,1,Goto(default,s,1)
+;
+; We also create an example user, 1234, who is on the console and has
+; voicemail, etc.
+;
+exten => 1234,1,Playback(transfer,skip)		; "Please hold while..."
+					; (but skip if channel is not up)
+exten => 1234,n,Gosub(${EXTEN},stdexten(${GLOBAL(CONSOLE)}))
+exten => 1234,n,Goto(default,s,1)		; exited Voicemail
+
+exten => 1235,1,Voicemail(1234,u)		; Right to voicemail
+
+exten => 1236,1,Dial(Console/dsp)		; Ring forever
+exten => 1236,n,Voicemail(1234,b)		; Unless busy
+
+;
+; # for when they're done with the demo
+;
+exten => #,1,Playback(demo-thanks)	; "Thanks for trying the demo"
+exten => #,n,Hangup			; Hang them up.
+
+;
+; A timeout and "invalid extension rule"
+;
+exten => t,1,Goto(#,1)			; If they take too long, give up
+exten => i,1,Playback(invalid)		; "That's not valid, try again"
+
+;
+; Create an extension, 500, for dialing the
+; Asterisk demo.
+;
+exten => 500,1,Playback(demo-abouttotry); Let them know what's going on
+exten => 500,n,Dial(IAX2/guest@pbx.digium.com/s@default)	; Call the Asterisk demo
+exten => 500,n,Playback(demo-nogo)	; Couldn't connect to the demo site
+exten => 500,n,Goto(s,6)		; Return to the start over message.
+
+;
+; Create an extension, 600, for evaluating echo latency.
+;
+exten => 600,1,Playback(demo-echotest)	; Let them know what's going on
+exten => 600,n,Echo			; Do the echo test
+exten => 600,n,Playback(demo-echodone)	; Let them know it's over
+exten => 600,n,Goto(s,6)		; Start over
+
+;
+;	You can use the Macro Page to intercom a individual user
+exten => 76245,1,Macro(page,SIP/Grandstream1)
+; or if your peernames are the same as extensions
+exten => _7XXX,1,Macro(page,SIP/${EXTEN})
+;
+;
+; System Wide Page at extension 7999
+;
+exten => 7999,1,Set(TIMEOUT(absolute)=60)
+exten => 7999,2,Page(Local/Grandstream1@page&Local/Xlite1@page&Local/1234@page/n,d)
+
+; Give voicemail at extension 8500
+;
+exten => 8500,1,VoicemailMain
+exten => 8500,n,Goto(s,6)
+
+    </textarea></form>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        mode: "text/x-asterisk",
+        matchBrackets: true,
+        lineNumbers: true
+      });
+    </script>
+
+    <p><strong>MIME types defined:</strong> <code>text/x-asterisk</code>.</p>
+
+  </article>

+ 77 - 0
libs/codemirror/mode/brainfuck/brainfuck.js

@@ -0,0 +1,77 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Brainfuck mode created by Michael Kaminsky https://github.com/mkaminsky11
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object') mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd) define(['../../lib/codemirror'], mod)
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+    var reserve = '><+-.,[]'.split('')
+    /*
+  comments can be either:
+  placed behind lines
+
+        +++    this is a comment
+
+  where reserved characters cannot be used
+  or in a loop
+  [
+    this is ok to use [ ] and stuff
+  ]
+  or preceded by #
+  */
+    CodeMirror.defineMode('brainfuck', function () {
+        return {
+            startState: function () {
+                return {
+                    commentLine: false,
+                    left: 0,
+                    right: 0,
+                    commentLoop: false,
+                }
+            },
+            token: function (stream, state) {
+                if (stream.eatSpace()) return null
+                if (stream.sol()) {
+                    state.commentLine = false
+                }
+                var ch = stream.next().toString()
+                if (reserve.indexOf(ch) !== -1) {
+                    if (state.commentLine === true) {
+                        if (stream.eol()) {
+                            state.commentLine = false
+                        }
+                        return 'comment'
+                    }
+                    if (ch === ']' || ch === '[') {
+                        if (ch === '[') {
+                            state.left++
+                        } else {
+                            state.right++
+                        }
+                        return 'bracket'
+                    } else if (ch === '+' || ch === '-') {
+                        return 'keyword'
+                    } else if (ch === '<' || ch === '>') {
+                        return 'atom'
+                    } else if (ch === '.' || ch === ',') {
+                        return 'def'
+                    }
+                } else {
+                    state.commentLine = true
+                    if (stream.eol()) {
+                        state.commentLine = false
+                    }
+                    return 'comment'
+                }
+                if (stream.eol()) {
+                    state.commentLine = false
+                }
+            },
+        }
+    })
+    CodeMirror.defineMIME('text/x-brainfuck', 'brainfuck')
+})

+ 85 - 0
libs/codemirror/mode/brainfuck/index.html

@@ -0,0 +1,85 @@
+<!doctype html>
+
+<title>CodeMirror: Brainfuck mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<script src="./brainfuck.js"></script>
+<style>
+	.CodeMirror { border: 2px inset #dee; }
+    </style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#"></a>
+  </ul>
+</div>
+
+<article>
+<h2>Brainfuck mode</h2>
+<form><textarea id="code" name="code">
+[ This program prints "Hello World!" and a newline to the screen, its
+  length is 106 active command characters [it is not the shortest.]
+
+  This loop is a "comment loop", it's a simple way of adding a comment
+  to a BF program such that you don't have to worry about any command
+  characters. Any ".", ",", "+", "-", "&lt;" and "&gt;" characters are simply
+  ignored, the "[" and "]" characters just have to be balanced.
+]
++++++ +++               Set Cell #0 to 8
+[
+    &gt;++++               Add 4 to Cell #1; this will always set Cell #1 to 4
+    [                   as the cell will be cleared by the loop
+        &gt;++             Add 2 to Cell #2
+        &gt;+++            Add 3 to Cell #3
+        &gt;+++            Add 3 to Cell #4
+        &gt;+              Add 1 to Cell #5
+        &lt;&lt;&lt;&lt;-           Decrement the loop counter in Cell #1
+    ]                   Loop till Cell #1 is zero; number of iterations is 4
+    &gt;+                  Add 1 to Cell #2
+    &gt;+                  Add 1 to Cell #3
+    &gt;-                  Subtract 1 from Cell #4
+    &gt;&gt;+                 Add 1 to Cell #6
+    [&lt;]                 Move back to the first zero cell you find; this will
+                        be Cell #1 which was cleared by the previous loop
+    &lt;-                  Decrement the loop Counter in Cell #0
+]                       Loop till Cell #0 is zero; number of iterations is 8
+
+The result of this is:
+Cell No :   0   1   2   3   4   5   6
+Contents:   0   0  72 104  88  32   8
+Pointer :   ^
+
+&gt;&gt;.                     Cell #2 has value 72 which is 'H'
+&gt;---.                   Subtract 3 from Cell #3 to get 101 which is 'e'
++++++++..+++.           Likewise for 'llo' from Cell #3
+&gt;&gt;.                     Cell #5 is 32 for the space
+&lt;-.                     Subtract 1 from Cell #4 for 87 to give a 'W'
+&lt;.                      Cell #3 was set to 'o' from the end of 'Hello'
++++.------.--------.    Cell #3 for 'rl' and 'd'
+&gt;&gt;+.                    Add 1 to Cell #5 gives us an exclamation point
+&gt;++.                    And finally a newline from Cell #6
+</textarea></form>
+
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        mode: "text/x-brainfuck"
+      });
+    </script>
+
+    <p>A mode for Brainfuck</p>
+
+    <p><strong>MIME types defined:</strong> <code>text/x-brainfuck</code></p>
+  </article>

+ 943 - 0
libs/codemirror/mode/clike/clike.js

@@ -0,0 +1,943 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    function Context(indented, column, type, info, align, prev) {
+        this.indented = indented
+        this.column = column
+        this.type = type
+        this.info = info
+        this.align = align
+        this.prev = prev
+    }
+    function pushContext(state, col, type, info) {
+        var indent = state.indented
+        if (state.context && state.context.type == 'statement' && type != 'statement') indent = state.context.indented
+        return (state.context = new Context(indent, col, type, info, null, state.context))
+    }
+    function popContext(state) {
+        var t = state.context.type
+        if (t == ')' || t == ']' || t == '}') state.indented = state.context.indented
+        return (state.context = state.context.prev)
+    }
+
+    function typeBefore(stream, state, pos) {
+        if (state.prevToken == 'variable' || state.prevToken == 'type') return true
+        if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true
+        if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true
+    }
+
+    function isTopScope(context) {
+        for (;;) {
+            if (!context || context.type == 'top') return true
+            if (context.type == '}' && context.prev.info != 'namespace') return false
+            context = context.prev
+        }
+    }
+
+    CodeMirror.defineMode('clike', function (config, parserConfig) {
+        var indentUnit = config.indentUnit,
+            statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
+            dontAlignCalls = parserConfig.dontAlignCalls,
+            keywords = parserConfig.keywords || {},
+            types = parserConfig.types || {},
+            builtin = parserConfig.builtin || {},
+            blockKeywords = parserConfig.blockKeywords || {},
+            defKeywords = parserConfig.defKeywords || {},
+            atoms = parserConfig.atoms || {},
+            hooks = parserConfig.hooks || {},
+            multiLineStrings = parserConfig.multiLineStrings,
+            indentStatements = parserConfig.indentStatements !== false,
+            indentSwitch = parserConfig.indentSwitch !== false,
+            namespaceSeparator = parserConfig.namespaceSeparator,
+            isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/,
+            numberStart = parserConfig.numberStart || /[\d\.]/,
+            number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
+            isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
+            isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/,
+            // An optional function that takes a {string} token and returns true if it
+            // should be treated as a builtin.
+            isReservedIdentifier = parserConfig.isReservedIdentifier || false
+
+        var curPunc, isDefKeyword
+
+        function tokenBase(stream, state) {
+            var ch = stream.next()
+            if (hooks[ch]) {
+                var result = hooks[ch](stream, state)
+                if (result !== false) return result
+            }
+            if (ch == '"' || ch == "'") {
+                state.tokenize = tokenString(ch)
+                return state.tokenize(stream, state)
+            }
+            if (numberStart.test(ch)) {
+                stream.backUp(1)
+                if (stream.match(number)) return 'number'
+                stream.next()
+            }
+            if (isPunctuationChar.test(ch)) {
+                curPunc = ch
+                return null
+            }
+            if (ch == '/') {
+                if (stream.eat('*')) {
+                    state.tokenize = tokenComment
+                    return tokenComment(stream, state)
+                }
+                if (stream.eat('/')) {
+                    stream.skipToEnd()
+                    return 'comment'
+                }
+            }
+            if (isOperatorChar.test(ch)) {
+                while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {}
+                return 'operator'
+            }
+            stream.eatWhile(isIdentifierChar)
+            if (namespaceSeparator) while (stream.match(namespaceSeparator)) stream.eatWhile(isIdentifierChar)
+
+            var cur = stream.current()
+            if (contains(keywords, cur)) {
+                if (contains(blockKeywords, cur)) curPunc = 'newstatement'
+                if (contains(defKeywords, cur)) isDefKeyword = true
+                return 'keyword'
+            }
+            if (contains(types, cur)) return 'type'
+            if (contains(builtin, cur) || (isReservedIdentifier && isReservedIdentifier(cur))) {
+                if (contains(blockKeywords, cur)) curPunc = 'newstatement'
+                return 'builtin'
+            }
+            if (contains(atoms, cur)) return 'atom'
+            return 'variable'
+        }
+
+        function tokenString(quote) {
+            return function (stream, state) {
+                var escaped = false,
+                    next,
+                    end = false
+                while ((next = stream.next()) != null) {
+                    if (next == quote && !escaped) {
+                        end = true
+                        break
+                    }
+                    escaped = !escaped && next == '\\'
+                }
+                if (end || !(escaped || multiLineStrings)) state.tokenize = null
+                return 'string'
+            }
+        }
+
+        function tokenComment(stream, state) {
+            var maybeEnd = false,
+                ch
+            while ((ch = stream.next())) {
+                if (ch == '/' && maybeEnd) {
+                    state.tokenize = null
+                    break
+                }
+                maybeEnd = ch == '*'
+            }
+            return 'comment'
+        }
+
+        function maybeEOL(stream, state) {
+            if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context)) state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)
+        }
+
+        // Interface
+
+        return {
+            startState: function (basecolumn) {
+                return {
+                    tokenize: null,
+                    context: new Context((basecolumn || 0) - indentUnit, 0, 'top', null, false),
+                    indented: 0,
+                    startOfLine: true,
+                    prevToken: null,
+                }
+            },
+
+            token: function (stream, state) {
+                var ctx = state.context
+                if (stream.sol()) {
+                    if (ctx.align == null) ctx.align = false
+                    state.indented = stream.indentation()
+                    state.startOfLine = true
+                }
+                if (stream.eatSpace()) {
+                    maybeEOL(stream, state)
+                    return null
+                }
+                curPunc = isDefKeyword = null
+                var style = (state.tokenize || tokenBase)(stream, state)
+                if (style == 'comment' || style == 'meta') return style
+                if (ctx.align == null) ctx.align = true
+
+                if (curPunc == ';' || curPunc == ':' || (curPunc == ',' && stream.match(/^\s*(?:\/\/.*)?$/, false))) while (state.context.type == 'statement') popContext(state)
+                else if (curPunc == '{') pushContext(state, stream.column(), '}')
+                else if (curPunc == '[') pushContext(state, stream.column(), ']')
+                else if (curPunc == '(') pushContext(state, stream.column(), ')')
+                else if (curPunc == '}') {
+                    while (ctx.type == 'statement') ctx = popContext(state)
+                    if (ctx.type == '}') ctx = popContext(state)
+                    while (ctx.type == 'statement') ctx = popContext(state)
+                } else if (curPunc == ctx.type) popContext(state)
+                else if (indentStatements && (((ctx.type == '}' || ctx.type == 'top') && curPunc != ';') || (ctx.type == 'statement' && curPunc == 'newstatement'))) {
+                    pushContext(state, stream.column(), 'statement', stream.current())
+                }
+
+                if (
+                    style == 'variable' &&
+                    (state.prevToken == 'def' || (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) && isTopScope(state.context) && stream.match(/^\s*\(/, false)))
+                )
+                    style = 'def'
+
+                if (hooks.token) {
+                    var result = hooks.token(stream, state, style)
+                    if (result !== undefined) style = result
+                }
+
+                if (style == 'def' && parserConfig.styleDefs === false) style = 'variable'
+
+                state.startOfLine = false
+                state.prevToken = isDefKeyword ? 'def' : style || curPunc
+                maybeEOL(stream, state)
+                return style
+            },
+
+            indent: function (state, textAfter) {
+                if ((state.tokenize != tokenBase && state.tokenize != null) || state.typeAtEndOfLine) return CodeMirror.Pass
+                var ctx = state.context,
+                    firstChar = textAfter && textAfter.charAt(0)
+                var closing = firstChar == ctx.type
+                if (ctx.type == 'statement' && firstChar == '}') ctx = ctx.prev
+                if (parserConfig.dontIndentStatements) while (ctx.type == 'statement' && parserConfig.dontIndentStatements.test(ctx.info)) ctx = ctx.prev
+                if (hooks.indent) {
+                    var hook = hooks.indent(state, ctx, textAfter, indentUnit)
+                    if (typeof hook == 'number') return hook
+                }
+                var switchBlock = ctx.prev && ctx.prev.info == 'switch'
+                if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
+                    while (ctx.type != 'top' && ctx.type != '}') ctx = ctx.prev
+                    return ctx.indented
+                }
+                if (ctx.type == 'statement') return ctx.indented + (firstChar == '{' ? 0 : statementIndentUnit)
+                if (ctx.align && (!dontAlignCalls || ctx.type != ')')) return ctx.column + (closing ? 0 : 1)
+                if (ctx.type == ')' && !closing) return ctx.indented + statementIndentUnit
+
+                return ctx.indented + (closing ? 0 : indentUnit) + (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0)
+            },
+
+            electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
+            blockCommentStart: '/*',
+            blockCommentEnd: '*/',
+            blockCommentContinue: ' * ',
+            lineComment: '//',
+            fold: 'brace',
+        }
+    })
+
+    function words(str) {
+        var obj = {},
+            words = str.split(' ')
+        for (var i = 0; i < words.length; ++i) obj[words[i]] = true
+        return obj
+    }
+    function contains(words, word) {
+        if (typeof words === 'function') {
+            return words(word)
+        } else {
+            return words.propertyIsEnumerable(word)
+        }
+    }
+    var cKeywords =
+        'auto if break case register continue return default do sizeof ' + 'static else struct switch extern typedef union for goto while enum const ' + 'volatile inline restrict asm fortran'
+
+    // Keywords from https://en.cppreference.com/w/cpp/keyword includes C++20.
+    var cppKeywords =
+        'alignas alignof and and_eq audit axiom bitand bitor catch ' +
+        'class compl concept constexpr const_cast decltype delete dynamic_cast ' +
+        'explicit export final friend import module mutable namespace new noexcept ' +
+        'not not_eq operator or or_eq override private protected public ' +
+        'reinterpret_cast requires static_assert static_cast template this ' +
+        'thread_local throw try typeid typename using virtual xor xor_eq'
+
+    var objCKeywords =
+        'bycopy byref in inout oneway out self super atomic nonatomic retain copy ' +
+        'readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd ' +
+        '@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class ' +
+        '@public @package @private @protected @required @optional @try @catch @finally @import ' +
+        '@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available'
+
+    var objCBuiltins =
+        'FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION ' +
+        ' NS_RETURNS_RETAINEDNS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER ' +
+        'NS_DESIGNATED_INITIALIZER NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION ' +
+        'NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT'
+
+    // Do not use this. Use the cTypes function below. This is global just to avoid
+    // excessive calls when cTypes is being called multiple times during a parse.
+    var basicCTypes = words('int long char short double float unsigned signed ' + 'void bool')
+
+    // Do not use this. Use the objCTypes function below. This is global just to avoid
+    // excessive calls when objCTypes is being called multiple times during a parse.
+    var basicObjCTypes = words('SEL instancetype id Class Protocol BOOL')
+
+    // Returns true if identifier is a "C" type.
+    // C type is defined as those that are reserved by the compiler (basicTypes),
+    // and those that end in _t (Reserved by POSIX for types)
+    // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html
+    function cTypes(identifier) {
+        return contains(basicCTypes, identifier) || /.+_t$/.test(identifier)
+    }
+
+    // Returns true if identifier is a "Objective C" type.
+    function objCTypes(identifier) {
+        return cTypes(identifier) || contains(basicObjCTypes, identifier)
+    }
+
+    var cBlockKeywords = 'case do else for if switch while struct enum union'
+    var cDefKeywords = 'struct enum union'
+
+    function cppHook(stream, state) {
+        if (!state.startOfLine) return false
+        for (var ch, next = null; (ch = stream.peek()); ) {
+            if (ch == '\\' && stream.match(/^.$/)) {
+                next = cppHook
+                break
+            } else if (ch == '/' && stream.match(/^\/[\/\*]/, false)) {
+                break
+            }
+            stream.next()
+        }
+        state.tokenize = next
+        return 'meta'
+    }
+
+    function pointerHook(_stream, state) {
+        if (state.prevToken == 'type') return 'type'
+        return false
+    }
+
+    // For C and C++ (and ObjC): identifiers starting with __
+    // or _ followed by a capital letter are reserved for the compiler.
+    function cIsReservedIdentifier(token) {
+        if (!token || token.length < 2) return false
+        if (token[0] != '_') return false
+        return token[1] == '_' || token[1] !== token[1].toLowerCase()
+    }
+
+    function cpp14Literal(stream) {
+        stream.eatWhile(/[\w\.']/)
+        return 'number'
+    }
+
+    function cpp11StringHook(stream, state) {
+        stream.backUp(1)
+        // Raw strings.
+        if (stream.match(/^(?:R|u8R|uR|UR|LR)/)) {
+            var match = stream.match(/^"([^\s\\()]{0,16})\(/)
+            if (!match) {
+                return false
+            }
+            state.cpp11RawStringDelim = match[1]
+            state.tokenize = tokenRawString
+            return tokenRawString(stream, state)
+        }
+        // Unicode strings/chars.
+        if (stream.match(/^(?:u8|u|U|L)/)) {
+            if (stream.match(/^["']/, /* eat */ false)) {
+                return 'string'
+            }
+            return false
+        }
+        // Ignore this hook.
+        stream.next()
+        return false
+    }
+
+    function cppLooksLikeConstructor(word) {
+        var lastTwo = /(\w+)::~?(\w+)$/.exec(word)
+        return lastTwo && lastTwo[1] == lastTwo[2]
+    }
+
+    // C#-style strings where "" escapes a quote.
+    function tokenAtString(stream, state) {
+        var next
+        while ((next = stream.next()) != null) {
+            if (next == '"' && !stream.eat('"')) {
+                state.tokenize = null
+                break
+            }
+        }
+        return 'string'
+    }
+
+    // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
+    // <delim> can be a string up to 16 characters long.
+    function tokenRawString(stream, state) {
+        // Escape characters that have special regex meanings.
+        var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&')
+        var match = stream.match(new RegExp('.*?\\)' + delim + '"'))
+        if (match) state.tokenize = null
+        else stream.skipToEnd()
+        return 'string'
+    }
+
+    function def(mimes, mode) {
+        if (typeof mimes == 'string') mimes = [mimes]
+        var words = []
+        function add(obj) {
+            if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop)) words.push(prop)
+        }
+        add(mode.keywords)
+        add(mode.types)
+        add(mode.builtin)
+        add(mode.atoms)
+        if (words.length) {
+            mode.helperType = mimes[0]
+            CodeMirror.registerHelper('hintWords', mimes[0], words)
+        }
+
+        for (var i = 0; i < mimes.length; ++i) CodeMirror.defineMIME(mimes[i], mode)
+    }
+
+    def(['text/x-csrc', 'text/x-c', 'text/x-chdr'], {
+        name: 'clike',
+        keywords: words(cKeywords),
+        types: cTypes,
+        blockKeywords: words(cBlockKeywords),
+        defKeywords: words(cDefKeywords),
+        typeFirstDefinitions: true,
+        atoms: words('NULL true false'),
+        isReservedIdentifier: cIsReservedIdentifier,
+        hooks: {
+            '#': cppHook,
+            '*': pointerHook,
+        },
+        modeProps: { fold: ['brace', 'include'] },
+    })
+
+    def(['text/x-c++src', 'text/x-c++hdr'], {
+        name: 'clike',
+        keywords: words(cKeywords + ' ' + cppKeywords),
+        types: cTypes,
+        blockKeywords: words(cBlockKeywords + ' class try catch'),
+        defKeywords: words(cDefKeywords + ' class namespace'),
+        typeFirstDefinitions: true,
+        atoms: words('true false NULL nullptr'),
+        dontIndentStatements: /^template$/,
+        isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
+        isReservedIdentifier: cIsReservedIdentifier,
+        hooks: {
+            '#': cppHook,
+            '*': pointerHook,
+            u: cpp11StringHook,
+            U: cpp11StringHook,
+            L: cpp11StringHook,
+            R: cpp11StringHook,
+            0: cpp14Literal,
+            1: cpp14Literal,
+            2: cpp14Literal,
+            3: cpp14Literal,
+            4: cpp14Literal,
+            5: cpp14Literal,
+            6: cpp14Literal,
+            7: cpp14Literal,
+            8: cpp14Literal,
+            9: cpp14Literal,
+            token: function (stream, state, style) {
+                if (style == 'variable' && stream.peek() == '(' && (state.prevToken == ';' || state.prevToken == null || state.prevToken == '}') && cppLooksLikeConstructor(stream.current()))
+                    return 'def'
+            },
+        },
+        namespaceSeparator: '::',
+        modeProps: { fold: ['brace', 'include'] },
+    })
+
+    def('text/x-java', {
+        name: 'clike',
+        keywords: words(
+            'abstract assert break case catch class const continue default ' +
+                'do else enum extends final finally for goto if implements import ' +
+                'instanceof interface native new package private protected public ' +
+                'return static strictfp super switch synchronized this throw throws transient ' +
+                'try volatile while @interface'
+        ),
+        types: words('var byte short int long float double boolean char void Boolean Byte Character Double Float ' + 'Integer Long Number Object Short String StringBuffer StringBuilder Void'),
+        blockKeywords: words('catch class do else finally for if switch try while'),
+        defKeywords: words('class interface enum @interface'),
+        typeFirstDefinitions: true,
+        atoms: words('true false null'),
+        number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+        hooks: {
+            '@': function (stream) {
+                // Don't match the @interface keyword.
+                if (stream.match('interface', false)) return false
+
+                stream.eatWhile(/[\w\$_]/)
+                return 'meta'
+            },
+            '"': function (stream, state) {
+                if (!stream.match('""\n')) return false
+                state.tokenize = tokenTripleString
+                return state.tokenize(stream, state)
+            },
+        },
+        modeProps: { fold: ['brace', 'import'] },
+    })
+
+    def('text/x-csharp', {
+        name: 'clike',
+        keywords: words(
+            'abstract as async await base break case catch checked class const continue' +
+                ' default delegate do else enum event explicit extern finally fixed for' +
+                ' foreach goto if implicit in interface internal is lock namespace new' +
+                ' operator out override params private protected public readonly ref return sealed' +
+                ' sizeof stackalloc static struct switch this throw try typeof unchecked' +
+                ' unsafe using virtual void volatile while add alias ascending descending dynamic from get' +
+                ' global group into join let orderby partial remove select set value var yield'
+        ),
+        types: words(
+            'Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func' +
+                ' Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32' +
+                ' UInt64 bool byte char decimal double short int long object' +
+                ' sbyte float string ushort uint ulong'
+        ),
+        blockKeywords: words('catch class do else finally for foreach if struct switch try while'),
+        defKeywords: words('class interface namespace struct var'),
+        typeFirstDefinitions: true,
+        atoms: words('true false null'),
+        hooks: {
+            '@': function (stream, state) {
+                if (stream.eat('"')) {
+                    state.tokenize = tokenAtString
+                    return tokenAtString(stream, state)
+                }
+                stream.eatWhile(/[\w\$_]/)
+                return 'meta'
+            },
+        },
+    })
+
+    function tokenTripleString(stream, state) {
+        var escaped = false
+        while (!stream.eol()) {
+            if (!escaped && stream.match('"""')) {
+                state.tokenize = null
+                break
+            }
+            escaped = stream.next() == '\\' && !escaped
+        }
+        return 'string'
+    }
+
+    function tokenNestedComment(depth) {
+        return function (stream, state) {
+            var ch
+            while ((ch = stream.next())) {
+                if (ch == '*' && stream.eat('/')) {
+                    if (depth == 1) {
+                        state.tokenize = null
+                        break
+                    } else {
+                        state.tokenize = tokenNestedComment(depth - 1)
+                        return state.tokenize(stream, state)
+                    }
+                } else if (ch == '/' && stream.eat('*')) {
+                    state.tokenize = tokenNestedComment(depth + 1)
+                    return state.tokenize(stream, state)
+                }
+            }
+            return 'comment'
+        }
+    }
+
+    def('text/x-scala', {
+        name: 'clike',
+        keywords: words(
+            /* scala */
+            'abstract case catch class def do else extends final finally for forSome if ' +
+                'implicit import lazy match new null object override package private protected return ' +
+                'sealed super this throw trait try type val var while with yield _ ' +
+                /* package scala */
+                'assert assume require print println printf readLine readBoolean readByte readShort ' +
+                'readChar readInt readLong readFloat readDouble'
+        ),
+        types: words(
+            'AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either ' +
+                'Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable ' +
+                'Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering ' +
+                'Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder ' +
+                'StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector ' +
+                /* package java.lang */
+                'Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable ' +
+                'Compiler Double Exception Float Integer Long Math Number Object Package Pair Process ' +
+                'Runtime Runnable SecurityManager Short StackTraceElement StrictMath String ' +
+                'StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void'
+        ),
+        multiLineStrings: true,
+        blockKeywords: words('catch class enum do else finally for forSome if match switch try while'),
+        defKeywords: words('class enum def object package trait type val var'),
+        atoms: words('true false null'),
+        indentStatements: false,
+        indentSwitch: false,
+        isOperatorChar: /[+\-*&%=<>!?|\/#:@]/,
+        hooks: {
+            '@': function (stream) {
+                stream.eatWhile(/[\w\$_]/)
+                return 'meta'
+            },
+            '"': function (stream, state) {
+                if (!stream.match('""')) return false
+                state.tokenize = tokenTripleString
+                return state.tokenize(stream, state)
+            },
+            "'": function (stream) {
+                stream.eatWhile(/[\w\$_\xa1-\uffff]/)
+                return 'atom'
+            },
+            '=': function (stream, state) {
+                var cx = state.context
+                if (cx.type == '}' && cx.align && stream.eat('>')) {
+                    state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
+                    return 'operator'
+                } else {
+                    return false
+                }
+            },
+
+            '/': function (stream, state) {
+                if (!stream.eat('*')) return false
+                state.tokenize = tokenNestedComment(1)
+                return state.tokenize(stream, state)
+            },
+        },
+        modeProps: { closeBrackets: { pairs: '()[]{}""', triples: '"' } },
+    })
+
+    function tokenKotlinString(tripleString) {
+        return function (stream, state) {
+            var escaped = false,
+                next,
+                end = false
+            while (!stream.eol()) {
+                if (!tripleString && !escaped && stream.match('"')) {
+                    end = true
+                    break
+                }
+                if (tripleString && stream.match('"""')) {
+                    end = true
+                    break
+                }
+                next = stream.next()
+                if (!escaped && next == '$' && stream.match('{')) stream.skipTo('}')
+                escaped = !escaped && next == '\\' && !tripleString
+            }
+            if (end || !tripleString) state.tokenize = null
+            return 'string'
+        }
+    }
+
+    def('text/x-kotlin', {
+        name: 'clike',
+        keywords: words(
+            /*keywords*/
+            'package as typealias class interface this super val operator ' +
+                'var fun for is in This throw return annotation ' +
+                'break continue object if else while do try when !in !is as? ' +
+                /*soft keywords*/
+                'file import where by get set abstract enum open inner override private public internal ' +
+                'protected catch finally out final vararg reified dynamic companion constructor init ' +
+                'sealed field property receiver param sparam lateinit data inline noinline tailrec ' +
+                'external annotation crossinline const operator infix suspend actual expect setparam value'
+        ),
+        types: words(
+            /* package java.lang */
+            'Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable ' +
+                'Compiler Double Exception Float Integer Long Math Number Object Package Pair Process ' +
+                'Runtime Runnable SecurityManager Short StackTraceElement StrictMath String ' +
+                'StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray ' +
+                'ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy ' +
+                'LazyThreadSafetyMode LongArray Nothing ShortArray Unit'
+        ),
+        intendSwitch: false,
+        indentStatements: false,
+        multiLineStrings: true,
+        number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+        blockKeywords: words('catch class do else finally for if where try while enum'),
+        defKeywords: words('class val var object interface fun'),
+        atoms: words('true false null this'),
+        hooks: {
+            '@': function (stream) {
+                stream.eatWhile(/[\w\$_]/)
+                return 'meta'
+            },
+            '*': function (_stream, state) {
+                return state.prevToken == '.' ? 'variable' : 'operator'
+            },
+            '"': function (stream, state) {
+                state.tokenize = tokenKotlinString(stream.match('""'))
+                return state.tokenize(stream, state)
+            },
+            '/': function (stream, state) {
+                if (!stream.eat('*')) return false
+                state.tokenize = tokenNestedComment(1)
+                return state.tokenize(stream, state)
+            },
+            indent: function (state, ctx, textAfter, indentUnit) {
+                var firstChar = textAfter && textAfter.charAt(0)
+                if ((state.prevToken == '}' || state.prevToken == ')') && textAfter == '') return state.indented
+                if (
+                    (state.prevToken == 'operator' && textAfter != '}' && state.context.type != '}') ||
+                    (state.prevToken == 'variable' && firstChar == '.') ||
+                    ((state.prevToken == '}' || state.prevToken == ')') && firstChar == '.')
+                )
+                    return indentUnit * 2 + ctx.indented
+                if (ctx.align && ctx.type == '}') return ctx.indented + (state.context.type == (textAfter || '').charAt(0) ? 0 : indentUnit)
+            },
+        },
+        modeProps: { closeBrackets: { triples: '"' } },
+    })
+
+    def(['x-shader/x-vertex', 'x-shader/x-fragment'], {
+        name: 'clike',
+        keywords: words(
+            'sampler1D sampler2D sampler3D samplerCube ' +
+                'sampler1DShadow sampler2DShadow ' +
+                'const attribute uniform varying ' +
+                'break continue discard return ' +
+                'for while do if else struct ' +
+                'in out inout'
+        ),
+        types: words('float int bool void ' + 'vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 ' + 'mat2 mat3 mat4'),
+        blockKeywords: words('for while do if else struct'),
+        builtin: words(
+            'radians degrees sin cos tan asin acos atan ' +
+                'pow exp log exp2 sqrt inversesqrt ' +
+                'abs sign floor ceil fract mod min max clamp mix step smoothstep ' +
+                'length distance dot cross normalize ftransform faceforward ' +
+                'reflect refract matrixCompMult ' +
+                'lessThan lessThanEqual greaterThan greaterThanEqual ' +
+                'equal notEqual any all not ' +
+                'texture1D texture1DProj texture1DLod texture1DProjLod ' +
+                'texture2D texture2DProj texture2DLod texture2DProjLod ' +
+                'texture3D texture3DProj texture3DLod texture3DProjLod ' +
+                'textureCube textureCubeLod ' +
+                'shadow1D shadow2D shadow1DProj shadow2DProj ' +
+                'shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod ' +
+                'dFdx dFdy fwidth ' +
+                'noise1 noise2 noise3 noise4'
+        ),
+        atoms: words(
+            'true false ' +
+                'gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex ' +
+                'gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 ' +
+                'gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 ' +
+                'gl_FogCoord gl_PointCoord ' +
+                'gl_Position gl_PointSize gl_ClipVertex ' +
+                'gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor ' +
+                'gl_TexCoord gl_FogFragCoord ' +
+                'gl_FragCoord gl_FrontFacing ' +
+                'gl_FragData gl_FragDepth ' +
+                'gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix ' +
+                'gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse ' +
+                'gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse ' +
+                'gl_TextureMatrixTranspose gl_ModelViewMatrixInverseTranspose ' +
+                'gl_ProjectionMatrixInverseTranspose ' +
+                'gl_ModelViewProjectionMatrixInverseTranspose ' +
+                'gl_TextureMatrixInverseTranspose ' +
+                'gl_NormalScale gl_DepthRange gl_ClipPlane ' +
+                'gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel ' +
+                'gl_FrontLightModelProduct gl_BackLightModelProduct ' +
+                'gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ ' +
+                'gl_FogParameters ' +
+                'gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords ' +
+                'gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats ' +
+                'gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits ' +
+                'gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits ' +
+                'gl_MaxDrawBuffers'
+        ),
+        indentSwitch: false,
+        hooks: { '#': cppHook },
+        modeProps: { fold: ['brace', 'include'] },
+    })
+
+    def('text/x-nesc', {
+        name: 'clike',
+        keywords: words(
+            cKeywords +
+                ' as atomic async call command component components configuration event generic ' +
+                'implementation includes interface module new norace nx_struct nx_union post provides ' +
+                'signal task uses abstract extends'
+        ),
+        types: cTypes,
+        blockKeywords: words(cBlockKeywords),
+        atoms: words('null true false'),
+        hooks: { '#': cppHook },
+        modeProps: { fold: ['brace', 'include'] },
+    })
+
+    def('text/x-objectivec', {
+        name: 'clike',
+        keywords: words(cKeywords + ' ' + objCKeywords),
+        types: objCTypes,
+        builtin: words(objCBuiltins),
+        blockKeywords: words(cBlockKeywords + ' @synthesize @try @catch @finally @autoreleasepool @synchronized'),
+        defKeywords: words(cDefKeywords + ' @interface @implementation @protocol @class'),
+        dontIndentStatements: /^@.*$/,
+        typeFirstDefinitions: true,
+        atoms: words('YES NO NULL Nil nil true false nullptr'),
+        isReservedIdentifier: cIsReservedIdentifier,
+        hooks: {
+            '#': cppHook,
+            '*': pointerHook,
+        },
+        modeProps: { fold: ['brace', 'include'] },
+    })
+
+    def('text/x-objectivec++', {
+        name: 'clike',
+        keywords: words(cKeywords + ' ' + objCKeywords + ' ' + cppKeywords),
+        types: objCTypes,
+        builtin: words(objCBuiltins),
+        blockKeywords: words(cBlockKeywords + ' @synthesize @try @catch @finally @autoreleasepool @synchronized class try catch'),
+        defKeywords: words(cDefKeywords + ' @interface @implementation @protocol @class class namespace'),
+        dontIndentStatements: /^@.*$|^template$/,
+        typeFirstDefinitions: true,
+        atoms: words('YES NO NULL Nil nil true false nullptr'),
+        isReservedIdentifier: cIsReservedIdentifier,
+        hooks: {
+            '#': cppHook,
+            '*': pointerHook,
+            u: cpp11StringHook,
+            U: cpp11StringHook,
+            L: cpp11StringHook,
+            R: cpp11StringHook,
+            0: cpp14Literal,
+            1: cpp14Literal,
+            2: cpp14Literal,
+            3: cpp14Literal,
+            4: cpp14Literal,
+            5: cpp14Literal,
+            6: cpp14Literal,
+            7: cpp14Literal,
+            8: cpp14Literal,
+            9: cpp14Literal,
+            token: function (stream, state, style) {
+                if (style == 'variable' && stream.peek() == '(' && (state.prevToken == ';' || state.prevToken == null || state.prevToken == '}') && cppLooksLikeConstructor(stream.current()))
+                    return 'def'
+            },
+        },
+        namespaceSeparator: '::',
+        modeProps: { fold: ['brace', 'include'] },
+    })
+
+    def('text/x-squirrel', {
+        name: 'clike',
+        keywords: words('base break clone continue const default delete enum extends function in class' + ' foreach local resume return this throw typeof yield constructor instanceof static'),
+        types: cTypes,
+        blockKeywords: words('case catch class else for foreach if switch try while'),
+        defKeywords: words('function local class'),
+        typeFirstDefinitions: true,
+        atoms: words('true false null'),
+        hooks: { '#': cppHook },
+        modeProps: { fold: ['brace', 'include'] },
+    })
+
+    // Ceylon Strings need to deal with interpolation
+    var stringTokenizer = null
+    function tokenCeylonString(type) {
+        return function (stream, state) {
+            var escaped = false,
+                next,
+                end = false
+            while (!stream.eol()) {
+                if (!escaped && stream.match('"') && (type == 'single' || stream.match('""'))) {
+                    end = true
+                    break
+                }
+                if (!escaped && stream.match('``')) {
+                    stringTokenizer = tokenCeylonString(type)
+                    end = true
+                    break
+                }
+                next = stream.next()
+                escaped = type == 'single' && !escaped && next == '\\'
+            }
+            if (end) state.tokenize = null
+            return 'string'
+        }
+    }
+
+    def('text/x-ceylon', {
+        name: 'clike',
+        keywords: words(
+            'abstracts alias assembly assert assign break case catch class continue dynamic else' +
+                ' exists extends finally for function given if import in interface is let module new' +
+                ' nonempty object of out outer package return satisfies super switch then this throw' +
+                ' try value void while'
+        ),
+        types: function (word) {
+            // In Ceylon all identifiers that start with an uppercase are types
+            var first = word.charAt(0)
+            return first === first.toUpperCase() && first !== first.toLowerCase()
+        },
+        blockKeywords: words('case catch class dynamic else finally for function if interface module new object switch try while'),
+        defKeywords: words('class dynamic function interface module object package value'),
+        builtin: words(
+            'abstract actual aliased annotation by default deprecated doc final formal late license' + ' native optional sealed see serializable shared suppressWarnings tagged throws variable'
+        ),
+        isPunctuationChar: /[\[\]{}\(\),;\:\.`]/,
+        isOperatorChar: /[+\-*&%=<>!?|^~:\/]/,
+        numberStart: /[\d#$]/,
+        number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,
+        multiLineStrings: true,
+        typeFirstDefinitions: true,
+        atoms: words('true false null larger smaller equal empty finished'),
+        indentSwitch: false,
+        styleDefs: false,
+        hooks: {
+            '@': function (stream) {
+                stream.eatWhile(/[\w\$_]/)
+                return 'meta'
+            },
+            '"': function (stream, state) {
+                state.tokenize = tokenCeylonString(stream.match('""') ? 'triple' : 'single')
+                return state.tokenize(stream, state)
+            },
+            '`': function (stream, state) {
+                if (!stringTokenizer || !stream.match('`')) return false
+                state.tokenize = stringTokenizer
+                stringTokenizer = null
+                return state.tokenize(stream, state)
+            },
+            "'": function (stream) {
+                stream.eatWhile(/[\w\$_\xa1-\uffff]/)
+                return 'atom'
+            },
+            token: function (_stream, state, style) {
+                if ((style == 'variable' || style == 'type') && state.prevToken == '.') {
+                    return 'variable-2'
+                }
+            },
+        },
+        modeProps: {
+            fold: ['brace', 'import'],
+            closeBrackets: { triples: '"' },
+        },
+    })
+})

+ 380 - 0
libs/codemirror/mode/clike/index.html

@@ -0,0 +1,380 @@
+<!doctype html>
+
+<title>CodeMirror: C-like mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<link rel="stylesheet" href="../../addon/hint/show-hint.css">
+<script src="../../addon/hint/show-hint.js"></script>
+<script src="clike.js"></script>
+<style>.CodeMirror {border: 2px inset #dee;}</style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">C-like</a>
+  </ul>
+</div>
+
+<article>
+<h2>C-like mode</h2>
+
+<div><textarea id="c-code">
+/* C demo code */
+
+#include <zmq.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <time.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <malloc.h>
+
+typedef struct {
+  void* arg_socket;
+  zmq_msg_t* arg_msg;
+  char* arg_string;
+  unsigned long arg_len;
+  int arg_int, arg_command;
+
+  int signal_fd;
+  int pad;
+  void* context;
+  sem_t sem;
+} acl_zmq_context;
+
+#define p(X) (context->arg_##X)
+
+void* zmq_thread(void* context_pointer) {
+  acl_zmq_context* context = (acl_zmq_context*)context_pointer;
+  char ok = 'K', err = 'X';
+  int res;
+
+  while (1) {
+    while ((res = sem_wait(&amp;context->sem)) == EINTR);
+    if (res) {write(context->signal_fd, &amp;err, 1); goto cleanup;}
+    switch(p(command)) {
+    case 0: goto cleanup;
+    case 1: p(socket) = zmq_socket(context->context, p(int)); break;
+    case 2: p(int) = zmq_close(p(socket)); break;
+    case 3: p(int) = zmq_bind(p(socket), p(string)); break;
+    case 4: p(int) = zmq_connect(p(socket), p(string)); break;
+    case 5: p(int) = zmq_getsockopt(p(socket), p(int), (void*)p(string), &amp;p(len)); break;
+    case 6: p(int) = zmq_setsockopt(p(socket), p(int), (void*)p(string), p(len)); break;
+    case 7: p(int) = zmq_send(p(socket), p(msg), p(int)); break;
+    case 8: p(int) = zmq_recv(p(socket), p(msg), p(int)); break;
+    case 9: p(int) = zmq_poll(p(socket), p(int), p(len)); break;
+    }
+    p(command) = errno;
+    write(context->signal_fd, &amp;ok, 1);
+  }
+ cleanup:
+  close(context->signal_fd);
+  free(context_pointer);
+  return 0;
+}
+
+void* zmq_thread_init(void* zmq_context, int signal_fd) {
+  acl_zmq_context* context = malloc(sizeof(acl_zmq_context));
+  pthread_t thread;
+
+  context->context = zmq_context;
+  context->signal_fd = signal_fd;
+  sem_init(&amp;context->sem, 1, 0);
+  pthread_create(&amp;thread, 0, &amp;zmq_thread, context);
+  pthread_detach(thread);
+  return context;
+}
+</textarea></div>
+
+<h2>C++ example</h2>
+
+<div><textarea id="cpp-code">
+#include <iostream>
+#include "mystuff/util.h"
+
+namespace {
+enum Enum {
+  VAL1, VAL2, VAL3
+};
+
+char32_t unicode_string = U"\U0010FFFF";
+string raw_string = R"delim(anything
+you
+want)delim";
+
+int Helper(const MyType& param) {
+  return 0;
+}
+} // namespace
+
+class ForwardDec;
+
+template <class T, class V>
+class Class : public BaseClass {
+  const MyType<T, V> member_;
+
+ public:
+  const MyType<T, V>& Method() const {
+    return member_;
+  }
+
+  void Method2(MyType<T, V>* value);
+}
+
+template <class T, class V>
+void Class::Method2(MyType<T, V>* value) {
+  std::out << 1 >> method();
+  value->Method3(member_);
+  member_ = value;
+}
+</textarea></div>
+
+<h2>Objective-C example</h2>
+
+<div><textarea id="objectivec-code">
+/*
+This is a longer comment
+That spans two lines
+*/
+
+#import "MyClass.h"
+#import <AFramework/AFramework.h>
+@import BFrameworkModule;
+
+NS_ENUM(SomeValues) {
+  aValue = 1;
+};
+
+// A Class Extension with some properties
+@interface MyClass ()<AProtocol>
+@property(atomic, readwrite, assign) NSInteger anInt;
+@property(nonatomic, strong, nullable) NSString *aString;
+@end
+
+@implementation YourAppDelegate
+
+- (instancetype)initWithString:(NSString *)aStringVar {
+  if ((self = [super init])) {
+    aString = aStringVar;
+  }
+  return self;
+}
+
+- (BOOL)doSomething:(float)progress {
+  NSString *myString = @"This is a ObjC string %f ";
+  myString = [[NSString stringWithFormat:myString, progress] stringByAppendingString:self.aString];
+  return myString.length > 100 ? NO : YES;
+}
+
+@end
+</textarea></div>
+
+<h2>Java example</h2>
+
+<div><textarea id="java-code">
+import com.demo.util.MyType;
+import com.demo.util.MyInterface;
+
+public enum Enum {
+  VAL1, VAL2, VAL3
+}
+
+public class Class<T, V> implements MyInterface {
+  public static final MyType<T, V> member;
+  
+  private class InnerClass {
+    public int zero() {
+      return 0;
+    }
+  }
+
+  @Override
+  public MyType method() {
+    return member;
+  }
+
+  public void method2(MyType<T, V> value) {
+    method();
+    value.method3();
+    member = value;
+  }
+}
+</textarea></div>
+
+<h2>Scala example</h2>
+
+<div><textarea id="scala-code">
+object FilterTest extends App {
+  def filter(xs: List[Int], threshold: Int) = {
+    def process(ys: List[Int]): List[Int] =
+      if (ys.isEmpty) ys
+      else if (ys.head < threshold) ys.head :: process(ys.tail)
+      else process(ys.tail)
+    process(xs)
+  }
+  println(filter(List(1, 9, 2, 8, 3, 7, 4), 5))
+}
+</textarea></div>
+
+<h2>Kotlin mode</h2>
+
+<div><textarea id="kotlin-code">
+package org.wasabi.http
+
+import java.util.concurrent.Executors
+import java.net.InetSocketAddress
+import org.wasabi.app.AppConfiguration
+import io.netty.bootstrap.ServerBootstrap
+import io.netty.channel.nio.NioEventLoopGroup
+import io.netty.channel.socket.nio.NioServerSocketChannel
+import org.wasabi.app.AppServer
+
+public class HttpServer(private val appServer: AppServer) {
+
+    val bootstrap: ServerBootstrap
+    val primaryGroup: NioEventLoopGroup
+    val workerGroup:  NioEventLoopGroup
+
+    init {
+        // Define worker groups
+        primaryGroup = NioEventLoopGroup()
+        workerGroup = NioEventLoopGroup()
+
+        // Initialize bootstrap of server
+        bootstrap = ServerBootstrap()
+
+        bootstrap.group(primaryGroup, workerGroup)
+        bootstrap.channel(javaClass<NioServerSocketChannel>())
+        bootstrap.childHandler(NettyPipelineInitializer(appServer))
+    }
+
+    public fun start(wait: Boolean = true) {
+        val channel = bootstrap.bind(appServer.configuration.port)?.sync()?.channel()
+
+        if (wait) {
+            channel?.closeFuture()?.sync()
+        }
+    }
+
+    public fun stop() {
+        // Shutdown all event loops
+        primaryGroup.shutdownGracefully()
+        workerGroup.shutdownGracefully()
+
+        // Wait till all threads are terminated
+        primaryGroup.terminationFuture().sync()
+        workerGroup.terminationFuture().sync()
+    }
+}
+</textarea></div>
+
+<h2>Ceylon mode</h2>
+
+<div><textarea id="ceylon-code">
+"Produces the [[stream|Iterable]] that results from repeated
+ application of the given [[function|next]] to the given
+ [[first]] element of the stream, until the function first
+ returns [[finished]]. If the given function never returns 
+ `finished`, the resulting stream is infinite.
+
+ For example:
+
+     loop(0)(2.plus).takeWhile(10.largerThan)
+
+ produces the stream `{ 0, 2, 4, 6, 8 }`."
+tagged("Streams")
+shared {Element+} loop&lt;Element&gt;(
+        "The first element of the resulting stream."
+        Element first)(
+        "The function that produces the next element of the
+         stream, given the current element. The function may
+         return [[finished]] to indicate the end of the 
+         stream."
+        Element|Finished next(Element element))
+    =&gt; let (start = first)
+    object satisfies {Element+} {
+        first =&gt; start;
+        empty =&gt; false;
+        function nextElement(Element element)
+                =&gt; next(element);
+        iterator()
+                =&gt; object satisfies Iterator&lt;Element&gt; {
+            variable Element|Finished current = start;
+            shared actual Element|Finished next() {
+                if (!is Finished result = current) {
+                    current = nextElement(result);
+                    return result;
+                }
+                else {
+                    return finished;
+                }
+            }
+        };
+    };
+</textarea></div>
+
+    <script>
+      var cEditor = CodeMirror.fromTextArea(document.getElementById("c-code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        mode: "text/x-csrc"
+      });
+      var cppEditor = CodeMirror.fromTextArea(document.getElementById("cpp-code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        mode: "text/x-c++src"
+      });
+      var javaEditor = CodeMirror.fromTextArea(document.getElementById("java-code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        mode: "text/x-java"
+      });
+      var objectivecEditor = CodeMirror.fromTextArea(document.getElementById("objectivec-code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        mode: "text/x-objectivec"
+      });
+      var scalaEditor = CodeMirror.fromTextArea(document.getElementById("scala-code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        mode: "text/x-scala"
+      });
+      var kotlinEditor = CodeMirror.fromTextArea(document.getElementById("kotlin-code"), {
+          lineNumbers: true,
+          matchBrackets: true,
+          mode: "text/x-kotlin"
+      });
+      var ceylonEditor = CodeMirror.fromTextArea(document.getElementById("ceylon-code"), {
+          lineNumbers: true,
+          matchBrackets: true,
+          mode: "text/x-ceylon"
+      });
+      var mac = CodeMirror.keyMap.default == CodeMirror.keyMap.macDefault;
+      CodeMirror.keyMap.default[(mac ? "Cmd" : "Ctrl") + "-Space"] = "autocomplete";
+    </script>
+
+    <p>Simple mode that tries to handle C-like languages as well as it
+    can. Takes two configuration parameters: <code>keywords</code>, an
+    object whose property names are the keywords in the language,
+    and <code>useCPP</code>, which determines whether C preprocessor
+    directives are recognized.</p>
+
+    <p><strong>MIME types defined:</strong> <code>text/x-csrc</code>
+    (C), <code>text/x-c++src</code> (C++), <code>text/x-java</code>
+    (Java), <code>text/x-csharp</code> (C#),
+    <code>text/x-objectivec</code> (Objective-C),
+    <code>text/x-scala</code> (Scala), <code>text/x-vertex</code>
+    <code>x-shader/x-fragment</code> (shader programs),
+    <code>text/x-squirrel</code> (Squirrel) and
+    <code>text/x-ceylon</code> (Ceylon)</p>
+</article>

+ 767 - 0
libs/codemirror/mode/clike/scala.html

@@ -0,0 +1,767 @@
+<!doctype html>
+
+<title>CodeMirror: Scala mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<link rel="stylesheet" href="../../theme/ambiance.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<script src="clike.js"></script>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">Scala</a>
+  </ul>
+</div>
+
+<article>
+<h2>Scala mode</h2>
+<form>
+<textarea id="code" name="code">
+
+  /*                     __                                               *\
+  **     ________ ___   / /  ___     Scala API                            **
+  **    / __/ __// _ | / /  / _ |    (c) 2003-2011, LAMP/EPFL             **
+  **  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
+  ** /____/\___/_/ |_/____/_/ | |                                         **
+  **                          |/                                          **
+  \*                                                                      */
+
+  package scala.collection
+
+  import generic._
+  import mutable.{ Builder, ListBuffer }
+  import annotation.{tailrec, migration, bridge}
+  import annotation.unchecked.{ uncheckedVariance => uV }
+  import parallel.ParIterable
+
+  /** A template trait for traversable collections of type `Traversable[A]`.
+   *  
+   *  $traversableInfo
+   *  @define mutability
+   *  @define traversableInfo
+   *  This is a base trait of all kinds of $mutability Scala collections. It
+   *  implements the behavior common to all collections, in terms of a method
+   *  `foreach` with signature:
+   * {{{
+   *     def foreach[U](f: Elem => U): Unit
+   * }}}
+   *  Collection classes mixing in this trait provide a concrete 
+   *  `foreach` method which traverses all the
+   *  elements contained in the collection, applying a given function to each.
+   *  They also need to provide a method `newBuilder`
+   *  which creates a builder for collections of the same kind.
+   *  
+   *  A traversable class might or might not have two properties: strictness
+   *  and orderedness. Neither is represented as a type.
+   *  
+   *  The instances of a strict collection class have all their elements
+   *  computed before they can be used as values. By contrast, instances of
+   *  a non-strict collection class may defer computation of some of their
+   *  elements until after the instance is available as a value.
+   *  A typical example of a non-strict collection class is a
+   *  <a href="../immutable/Stream.html" target="ContentFrame">
+   *  `scala.collection.immutable.Stream`</a>.
+   *  A more general class of examples are `TraversableViews`.
+   *  
+   *  If a collection is an instance of an ordered collection class, traversing
+   *  its elements with `foreach` will always visit elements in the
+   *  same order, even for different runs of the program. If the class is not
+   *  ordered, `foreach` can visit elements in different orders for
+   *  different runs (but it will keep the same order in the same run).'
+   * 
+   *  A typical example of a collection class which is not ordered is a
+   *  `HashMap` of objects. The traversal order for hash maps will
+   *  depend on the hash codes of its elements, and these hash codes might
+   *  differ from one run to the next. By contrast, a `LinkedHashMap`
+   *  is ordered because it's `foreach` method visits elements in the
+   *  order they were inserted into the `HashMap`.
+   *
+   *  @author Martin Odersky
+   *  @version 2.8
+   *  @since   2.8
+   *  @tparam A    the element type of the collection
+   *  @tparam Repr the type of the actual collection containing the elements.
+   *
+   *  @define Coll Traversable
+   *  @define coll traversable collection
+   */
+  trait TraversableLike[+A, +Repr] extends HasNewBuilder[A, Repr] 
+                                      with FilterMonadic[A, Repr]
+                                      with TraversableOnce[A]
+                                      with GenTraversableLike[A, Repr]
+                                      with Parallelizable[A, ParIterable[A]]
+  {
+    self =>
+
+    import Traversable.breaks._
+
+    /** The type implementing this traversable */
+    protected type Self = Repr
+
+    /** The collection of type $coll underlying this `TraversableLike` object.
+     *  By default this is implemented as the `TraversableLike` object itself,
+     *  but this can be overridden.
+     */
+    def repr: Repr = this.asInstanceOf[Repr]
+
+    /** The underlying collection seen as an instance of `$Coll`.
+     *  By default this is implemented as the current collection object itself,
+     *  but this can be overridden.
+     */
+    protected[this] def thisCollection: Traversable[A] = this.asInstanceOf[Traversable[A]]
+
+    /** A conversion from collections of type `Repr` to `$Coll` objects.
+     *  By default this is implemented as just a cast, but this can be overridden.
+     */
+    protected[this] def toCollection(repr: Repr): Traversable[A] = repr.asInstanceOf[Traversable[A]]
+
+    /** Creates a new builder for this collection type.
+     */
+    protected[this] def newBuilder: Builder[A, Repr]
+
+    protected[this] def parCombiner = ParIterable.newCombiner[A]
+
+    /** Applies a function `f` to all elements of this $coll.
+     *  
+     *    Note: this method underlies the implementation of most other bulk operations.
+     *    It's important to implement this method in an efficient way.
+     *  
+     *
+     *  @param  f   the function that is applied for its side-effect to every element.
+     *              The result of function `f` is discarded.
+     *              
+     *  @tparam  U  the type parameter describing the result of function `f`. 
+     *              This result will always be ignored. Typically `U` is `Unit`,
+     *              but this is not necessary.
+     *
+     *  @usecase def foreach(f: A => Unit): Unit
+     */
+    def foreach[U](f: A => U): Unit
+
+    /** Tests whether this $coll is empty.
+     *
+     *  @return    `true` if the $coll contain no elements, `false` otherwise.
+     */
+    def isEmpty: Boolean = {
+      var result = true
+      breakable {
+        for (x <- this) {
+          result = false
+          break
+        }
+      }
+      result
+    }
+
+    /** Tests whether this $coll is known to have a finite size.
+     *  All strict collections are known to have finite size. For a non-strict collection
+     *  such as `Stream`, the predicate returns `true` if all elements have been computed.
+     *  It returns `false` if the stream is not yet evaluated to the end.
+     *
+     *  Note: many collection methods will not work on collections of infinite sizes. 
+     *
+     *  @return  `true` if this collection is known to have finite size, `false` otherwise.
+     */
+    def hasDefiniteSize = true
+
+    def ++[B >: A, That](that: GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+      val b = bf(repr)
+      if (that.isInstanceOf[IndexedSeqLike[_, _]]) b.sizeHint(this, that.seq.size)
+      b ++= thisCollection
+      b ++= that.seq
+      b.result
+    }
+
+    @bridge
+    def ++[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That =
+      ++(that: GenTraversableOnce[B])(bf)
+
+    /** Concatenates this $coll with the elements of a traversable collection.
+     *  It differs from ++ in that the right operand determines the type of the
+     *  resulting collection rather than the left one.
+     * 
+     *  @param that   the traversable to append.
+     *  @tparam B     the element type of the returned collection. 
+     *  @tparam That  $thatinfo
+     *  @param bf     $bfinfo
+     *  @return       a new collection of type `That` which contains all elements
+     *                of this $coll followed by all elements of `that`.
+     * 
+     *  @usecase def ++:[B](that: TraversableOnce[B]): $Coll[B]
+     *  
+     *  @return       a new $coll which contains all elements of this $coll
+     *                followed by all elements of `that`.
+     */
+    def ++:[B >: A, That](that: TraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+      val b = bf(repr)
+      if (that.isInstanceOf[IndexedSeqLike[_, _]]) b.sizeHint(this, that.size)
+      b ++= that
+      b ++= thisCollection
+      b.result
+    }
+
+    /** This overload exists because: for the implementation of ++: we should reuse
+     *  that of ++ because many collections override it with more efficient versions.
+     *  Since TraversableOnce has no '++' method, we have to implement that directly,
+     *  but Traversable and down can use the overload.
+     */
+    def ++:[B >: A, That](that: Traversable[B])(implicit bf: CanBuildFrom[Repr, B, That]): That =
+      (that ++ seq)(breakOut)
+
+    def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+      val b = bf(repr)
+      b.sizeHint(this) 
+      for (x <- this) b += f(x)
+      b.result
+    }
+
+    def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+      val b = bf(repr)
+      for (x <- this) b ++= f(x).seq
+      b.result
+    }
+
+    /** Selects all elements of this $coll which satisfy a predicate.
+     *
+     *  @param p     the predicate used to test elements.
+     *  @return      a new $coll consisting of all elements of this $coll that satisfy the given
+     *               predicate `p`. The order of the elements is preserved.
+     */
+    def filter(p: A => Boolean): Repr = {
+      val b = newBuilder
+      for (x <- this) 
+        if (p(x)) b += x
+      b.result
+    }
+
+    /** Selects all elements of this $coll which do not satisfy a predicate.
+     *
+     *  @param p     the predicate used to test elements.
+     *  @return      a new $coll consisting of all elements of this $coll that do not satisfy the given
+     *               predicate `p`. The order of the elements is preserved.
+     */
+    def filterNot(p: A => Boolean): Repr = filter(!p(_))
+
+    def collect[B, That](pf: PartialFunction[A, B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+      val b = bf(repr)
+      for (x <- this) if (pf.isDefinedAt(x)) b += pf(x)
+      b.result
+    }
+
+    /** Builds a new collection by applying an option-valued function to all
+     *  elements of this $coll on which the function is defined.
+     *
+     *  @param f      the option-valued function which filters and maps the $coll.
+     *  @tparam B     the element type of the returned collection.
+     *  @tparam That  $thatinfo
+     *  @param bf     $bfinfo
+     *  @return       a new collection of type `That` resulting from applying the option-valued function
+     *                `f` to each element and collecting all defined results.
+     *                The order of the elements is preserved.
+     *
+     *  @usecase def filterMap[B](f: A => Option[B]): $Coll[B]
+     *  
+     *  @param pf     the partial function which filters and maps the $coll.
+     *  @return       a new $coll resulting from applying the given option-valued function
+     *                `f` to each element and collecting all defined results.
+     *                The order of the elements is preserved.
+    def filterMap[B, That](f: A => Option[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+      val b = bf(repr)
+      for (x <- this) 
+        f(x) match {
+          case Some(y) => b += y
+          case _ =>
+        }
+      b.result
+    }
+     */
+
+    /** Partitions this $coll in two ${coll}s according to a predicate.
+     *
+     *  @param p the predicate on which to partition.
+     *  @return  a pair of ${coll}s: the first $coll consists of all elements that 
+     *           satisfy the predicate `p` and the second $coll consists of all elements
+     *           that don't. The relative order of the elements in the resulting ${coll}s
+     *           is the same as in the original $coll.
+     */
+    def partition(p: A => Boolean): (Repr, Repr) = {
+      val l, r = newBuilder
+      for (x <- this) (if (p(x)) l else r) += x
+      (l.result, r.result)
+    }
+
+    def groupBy[K](f: A => K): immutable.Map[K, Repr] = {
+      val m = mutable.Map.empty[K, Builder[A, Repr]]
+      for (elem <- this) {
+        val key = f(elem)
+        val bldr = m.getOrElseUpdate(key, newBuilder)
+        bldr += elem
+      }
+      val b = immutable.Map.newBuilder[K, Repr]
+      for ((k, v) <- m)
+        b += ((k, v.result))
+
+      b.result
+    }
+
+    /** Tests whether a predicate holds for all elements of this $coll.
+     *
+     *  $mayNotTerminateInf
+     *
+     *  @param   p     the predicate used to test elements.
+     *  @return        `true` if the given predicate `p` holds for all elements
+     *                 of this $coll, otherwise `false`.
+     */
+    def forall(p: A => Boolean): Boolean = {
+      var result = true
+      breakable {
+        for (x <- this)
+          if (!p(x)) { result = false; break }
+      }
+      result
+    }
+
+    /** Tests whether a predicate holds for some of the elements of this $coll.
+     *
+     *  $mayNotTerminateInf
+     *
+     *  @param   p     the predicate used to test elements.
+     *  @return        `true` if the given predicate `p` holds for some of the
+     *                 elements of this $coll, otherwise `false`.
+     */
+    def exists(p: A => Boolean): Boolean = {
+      var result = false
+      breakable {
+        for (x <- this)
+          if (p(x)) { result = true; break }
+      }
+      result
+    }
+
+    /** Finds the first element of the $coll satisfying a predicate, if any.
+     * 
+     *  $mayNotTerminateInf
+     *  $orderDependent
+     *
+     *  @param p    the predicate used to test elements.
+     *  @return     an option value containing the first element in the $coll
+     *              that satisfies `p`, or `None` if none exists.
+     */
+    def find(p: A => Boolean): Option[A] = {
+      var result: Option[A] = None
+      breakable {
+        for (x <- this)
+          if (p(x)) { result = Some(x); break }
+      }
+      result
+    }
+
+    def scan[B >: A, That](z: B)(op: (B, B) => B)(implicit cbf: CanBuildFrom[Repr, B, That]): That = scanLeft(z)(op)
+
+    def scanLeft[B, That](z: B)(op: (B, A) => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+      val b = bf(repr)
+      b.sizeHint(this, 1)
+      var acc = z
+      b += acc
+      for (x <- this) { acc = op(acc, x); b += acc }
+      b.result
+    }
+
+    @migration(2, 9,
+      "This scanRight definition has changed in 2.9.\n" +
+      "The previous behavior can be reproduced with scanRight.reverse."
+    )
+    def scanRight[B, That](z: B)(op: (A, B) => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+      var scanned = List(z)
+      var acc = z
+      for (x <- reversed) {
+        acc = op(x, acc)
+        scanned ::= acc
+      }
+      val b = bf(repr)
+      for (elem <- scanned) b += elem
+      b.result
+    }
+
+    /** Selects the first element of this $coll.
+     *  $orderDependent
+     *  @return  the first element of this $coll.
+     *  @throws `NoSuchElementException` if the $coll is empty.
+     */
+    def head: A = {
+      var result: () => A = () => throw new NoSuchElementException
+      breakable {
+        for (x <- this) {
+          result = () => x
+          break
+        }
+      }
+      result()
+    }
+
+    /** Optionally selects the first element.
+     *  $orderDependent
+     *  @return  the first element of this $coll if it is nonempty, `None` if it is empty.
+     */
+    def headOption: Option[A] = if (isEmpty) None else Some(head)
+
+    /** Selects all elements except the first.
+     *  $orderDependent
+     *  @return  a $coll consisting of all elements of this $coll
+     *           except the first one.
+     *  @throws `UnsupportedOperationException` if the $coll is empty.
+     */ 
+    override def tail: Repr = {
+      if (isEmpty) throw new UnsupportedOperationException("empty.tail")
+      drop(1)
+    }
+
+    /** Selects the last element.
+      * $orderDependent
+      * @return The last element of this $coll.
+      * @throws NoSuchElementException If the $coll is empty.
+      */
+    def last: A = {
+      var lst = head
+      for (x <- this)
+        lst = x
+      lst
+    }
+
+    /** Optionally selects the last element.
+     *  $orderDependent
+     *  @return  the last element of this $coll$ if it is nonempty, `None` if it is empty.
+     */
+    def lastOption: Option[A] = if (isEmpty) None else Some(last)
+
+    /** Selects all elements except the last.
+     *  $orderDependent
+     *  @return  a $coll consisting of all elements of this $coll
+     *           except the last one.
+     *  @throws `UnsupportedOperationException` if the $coll is empty.
+     */
+    def init: Repr = {
+      if (isEmpty) throw new UnsupportedOperationException("empty.init")
+      var lst = head
+      var follow = false
+      val b = newBuilder
+      b.sizeHint(this, -1)
+      for (x <- this.seq) {
+        if (follow) b += lst
+        else follow = true
+        lst = x
+      }
+      b.result
+    }
+
+    def take(n: Int): Repr = slice(0, n)
+
+    def drop(n: Int): Repr = 
+      if (n <= 0) {
+        val b = newBuilder
+        b.sizeHint(this)
+        b ++= thisCollection result
+      }
+      else sliceWithKnownDelta(n, Int.MaxValue, -n)
+
+    def slice(from: Int, until: Int): Repr = sliceWithKnownBound(math.max(from, 0), until)
+
+    // Precondition: from >= 0, until > 0, builder already configured for building.
+    private[this] def sliceInternal(from: Int, until: Int, b: Builder[A, Repr]): Repr = {
+      var i = 0
+      breakable {
+        for (x <- this.seq) {
+          if (i >= from) b += x
+          i += 1
+          if (i >= until) break
+        }
+      }
+      b.result
+    }
+    // Precondition: from >= 0
+    private[scala] def sliceWithKnownDelta(from: Int, until: Int, delta: Int): Repr = {
+      val b = newBuilder
+      if (until <= from) b.result
+      else {
+        b.sizeHint(this, delta)
+        sliceInternal(from, until, b)
+      }
+    }
+    // Precondition: from >= 0
+    private[scala] def sliceWithKnownBound(from: Int, until: Int): Repr = {
+      val b = newBuilder
+      if (until <= from) b.result
+      else {
+        b.sizeHintBounded(until - from, this)      
+        sliceInternal(from, until, b)
+      }
+    }
+
+    def takeWhile(p: A => Boolean): Repr = {
+      val b = newBuilder
+      breakable {
+        for (x <- this) {
+          if (!p(x)) break
+          b += x
+        }
+      }
+      b.result
+    }
+
+    def dropWhile(p: A => Boolean): Repr = {
+      val b = newBuilder
+      var go = false
+      for (x <- this) {
+        if (!p(x)) go = true
+        if (go) b += x
+      }
+      b.result
+    }
+
+    def span(p: A => Boolean): (Repr, Repr) = {
+      val l, r = newBuilder
+      var toLeft = true
+      for (x <- this) {
+        toLeft = toLeft && p(x)
+        (if (toLeft) l else r) += x
+      }
+      (l.result, r.result)
+    }
+
+    def splitAt(n: Int): (Repr, Repr) = {
+      val l, r = newBuilder
+      l.sizeHintBounded(n, this)
+      if (n >= 0) r.sizeHint(this, -n)
+      var i = 0
+      for (x <- this) {
+        (if (i < n) l else r) += x
+        i += 1
+      }
+      (l.result, r.result)
+    }
+
+    /** Iterates over the tails of this $coll. The first value will be this
+     *  $coll and the final one will be an empty $coll, with the intervening
+     *  values the results of successive applications of `tail`.
+     *
+     *  @return   an iterator over all the tails of this $coll
+     *  @example  `List(1,2,3).tails = Iterator(List(1,2,3), List(2,3), List(3), Nil)`
+     */  
+    def tails: Iterator[Repr] = iterateUntilEmpty(_.tail)
+
+    /** Iterates over the inits of this $coll. The first value will be this
+     *  $coll and the final one will be an empty $coll, with the intervening
+     *  values the results of successive applications of `init`.
+     *
+     *  @return  an iterator over all the inits of this $coll
+     *  @example  `List(1,2,3).inits = Iterator(List(1,2,3), List(1,2), List(1), Nil)`
+     */
+    def inits: Iterator[Repr] = iterateUntilEmpty(_.init)
+
+    /** Copies elements of this $coll to an array.
+     *  Fills the given array `xs` with at most `len` elements of
+     *  this $coll, starting at position `start`.
+     *  Copying will stop once either the end of the current $coll is reached,
+     *  or the end of the array is reached, or `len` elements have been copied.
+     *
+     *  $willNotTerminateInf
+     * 
+     *  @param  xs     the array to fill.
+     *  @param  start  the starting index.
+     *  @param  len    the maximal number of elements to copy.
+     *  @tparam B      the type of the elements of the array. 
+     * 
+     *
+     *  @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Unit
+     */
+    def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) {
+      var i = start
+      val end = (start + len) min xs.length
+      breakable {
+        for (x <- this) {
+          if (i >= end) break
+          xs(i) = x
+          i += 1
+        }
+      }
+    }
+
+    def toTraversable: Traversable[A] = thisCollection
+    def toIterator: Iterator[A] = toStream.iterator
+    def toStream: Stream[A] = toBuffer.toStream
+
+    /** Converts this $coll to a string.
+     *
+     *  @return   a string representation of this collection. By default this
+     *            string consists of the `stringPrefix` of this $coll,
+     *            followed by all elements separated by commas and enclosed in parentheses.
+     */
+    override def toString = mkString(stringPrefix + "(", ", ", ")")
+
+    /** Defines the prefix of this object's `toString` representation.
+     *
+     *  @return  a string representation which starts the result of `toString`
+     *           applied to this $coll. By default the string prefix is the
+     *           simple name of the collection class $coll.
+     */
+    def stringPrefix : String = {
+      var string = repr.asInstanceOf[AnyRef].getClass.getName
+      val idx1 = string.lastIndexOf('.' : Int)
+      if (idx1 != -1) string = string.substring(idx1 + 1)
+      val idx2 = string.indexOf('$')
+      if (idx2 != -1) string = string.substring(0, idx2)
+      string
+    }
+
+    /** Creates a non-strict view of this $coll.
+     * 
+     *  @return a non-strict view of this $coll.
+     */
+    def view = new TraversableView[A, Repr] {
+      protected lazy val underlying = self.repr
+      override def foreach[U](f: A => U) = self foreach f
+    }
+
+    /** Creates a non-strict view of a slice of this $coll.
+     *
+     *  Note: the difference between `view` and `slice` is that `view` produces
+     *        a view of the current $coll, whereas `slice` produces a new $coll.
+     * 
+     *  Note: `view(from, to)` is equivalent to `view.slice(from, to)`
+     *  $orderDependent
+     * 
+     *  @param from   the index of the first element of the view
+     *  @param until  the index of the element following the view
+     *  @return a non-strict view of a slice of this $coll, starting at index `from`
+     *  and extending up to (but not including) index `until`.
+     */
+    def view(from: Int, until: Int): TraversableView[A, Repr] = view.slice(from, until)
+
+    /** Creates a non-strict filter of this $coll.
+     *
+     *  Note: the difference between `c filter p` and `c withFilter p` is that
+     *        the former creates a new collection, whereas the latter only
+     *        restricts the domain of subsequent `map`, `flatMap`, `foreach`,
+     *        and `withFilter` operations.
+     *  $orderDependent
+     * 
+     *  @param p   the predicate used to test elements.
+     *  @return    an object of class `WithFilter`, which supports
+     *             `map`, `flatMap`, `foreach`, and `withFilter` operations.
+     *             All these operations apply to those elements of this $coll which
+     *             satisfy the predicate `p`.
+     */
+    def withFilter(p: A => Boolean): FilterMonadic[A, Repr] = new WithFilter(p)
+
+    /** A class supporting filtered operations. Instances of this class are
+     *  returned by method `withFilter`.
+     */
+    class WithFilter(p: A => Boolean) extends FilterMonadic[A, Repr] {
+
+      /** Builds a new collection by applying a function to all elements of the
+       *  outer $coll containing this `WithFilter` instance that satisfy predicate `p`.
+       *
+       *  @param f      the function to apply to each element.
+       *  @tparam B     the element type of the returned collection.
+       *  @tparam That  $thatinfo
+       *  @param bf     $bfinfo
+       *  @return       a new collection of type `That` resulting from applying
+       *                the given function `f` to each element of the outer $coll
+       *                that satisfies predicate `p` and collecting the results.
+       *
+       *  @usecase def map[B](f: A => B): $Coll[B] 
+       *  
+       *  @return       a new $coll resulting from applying the given function
+       *                `f` to each element of the outer $coll that satisfies
+       *                predicate `p` and collecting the results.
+       */
+      def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+        val b = bf(repr)
+        for (x <- self) 
+          if (p(x)) b += f(x)
+        b.result
+      }
+
+      /** Builds a new collection by applying a function to all elements of the
+       *  outer $coll containing this `WithFilter` instance that satisfy
+       *  predicate `p` and concatenating the results. 
+       *
+       *  @param f      the function to apply to each element.
+       *  @tparam B     the element type of the returned collection.
+       *  @tparam That  $thatinfo
+       *  @param bf     $bfinfo
+       *  @return       a new collection of type `That` resulting from applying
+       *                the given collection-valued function `f` to each element
+       *                of the outer $coll that satisfies predicate `p` and
+       *                concatenating the results.
+       *
+       *  @usecase def flatMap[B](f: A => TraversableOnce[B]): $Coll[B]
+       * 
+       *  @return       a new $coll resulting from applying the given collection-valued function
+       *                `f` to each element of the outer $coll that satisfies predicate `p` and concatenating the results.
+       */
+      def flatMap[B, That](f: A => GenTraversableOnce[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = {
+        val b = bf(repr)
+        for (x <- self) 
+          if (p(x)) b ++= f(x).seq
+        b.result
+      }
+
+      /** Applies a function `f` to all elements of the outer $coll containing
+       *  this `WithFilter` instance that satisfy predicate `p`.
+       *
+       *  @param  f   the function that is applied for its side-effect to every element.
+       *              The result of function `f` is discarded.
+       *              
+       *  @tparam  U  the type parameter describing the result of function `f`. 
+       *              This result will always be ignored. Typically `U` is `Unit`,
+       *              but this is not necessary.
+       *
+       *  @usecase def foreach(f: A => Unit): Unit
+       */   
+      def foreach[U](f: A => U): Unit = 
+        for (x <- self) 
+          if (p(x)) f(x)
+
+      /** Further refines the filter for this $coll.
+       *
+       *  @param q   the predicate used to test elements.
+       *  @return    an object of class `WithFilter`, which supports
+       *             `map`, `flatMap`, `foreach`, and `withFilter` operations.
+       *             All these operations apply to those elements of this $coll which
+       *             satisfy the predicate `q` in addition to the predicate `p`.
+       */
+      def withFilter(q: A => Boolean): WithFilter = 
+        new WithFilter(x => p(x) && q(x))
+    }
+
+    // A helper for tails and inits.
+    private def iterateUntilEmpty(f: Traversable[A @uV] => Traversable[A @uV]): Iterator[Repr] = {
+      val it = Iterator.iterate(thisCollection)(f) takeWhile (x => !x.isEmpty)
+      it ++ Iterator(Nil) map (newBuilder ++= _ result)
+    }
+  }
+
+
+</textarea>
+</form>
+
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        theme: "ambiance",
+        mode: "text/x-scala"
+      });
+    </script>
+  </article>

+ 155 - 0
libs/codemirror/mode/clike/test.js

@@ -0,0 +1,155 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function () {
+    var mode = CodeMirror.getMode({ indentUnit: 2 }, 'text/x-c')
+    function MT(name) {
+        test.mode(name, mode, Array.prototype.slice.call(arguments, 1))
+    }
+
+    MT(
+        'indent',
+        '[type void] [def foo]([type void*] [variable a], [type int] [variable b]) {',
+        '  [type int] [variable c] [operator =] [variable b] [operator +]',
+        '    [number 1];',
+        '  [keyword return] [operator *][variable a];',
+        '}'
+    )
+
+    MT(
+        'indent_switch',
+        '[keyword switch] ([variable x]) {',
+        '  [keyword case] [number 10]:',
+        '    [keyword return] [number 20];',
+        '  [keyword default]:',
+        '    [variable printf]([string "foo %c"], [variable x]);',
+        '}'
+    )
+
+    MT('def', '[type void] [def foo]() {}', '[keyword struct] [def bar]{}', '[keyword enum] [def zot]{}', '[keyword union] [def ugh]{}', '[type int] [type *][def baz]() {}')
+
+    MT(
+        'def_new_line',
+        '::[variable std]::[variable SomeTerribleType][operator <][variable T][operator >]',
+        '[def SomeLongMethodNameThatDoesntFitIntoOneLine]([keyword const] [variable MyType][operator &] [variable param]) {}'
+    )
+
+    MT('double_block', '[keyword for] (;;)', '  [keyword for] (;;)', '    [variable x][operator ++];', '[keyword return];')
+
+    MT(
+        'preprocessor',
+        '[meta #define FOO 3]',
+        '[type int] [variable foo];',
+        '[meta #define BAR\\]',
+        '[meta 4]',
+        '[type unsigned] [type int] [variable bar] [operator =] [number 8];',
+        '[meta #include <baz> ][comment // comment]'
+    )
+
+    MT('c_underscores', '[builtin __FOO];', '[builtin _Complex];', '[builtin __aName];', '[variable _aName];')
+
+    MT(
+        'c_types',
+        '[type int];',
+        '[type long];',
+        '[type char];',
+        '[type short];',
+        '[type double];',
+        '[type float];',
+        '[type unsigned];',
+        '[type signed];',
+        '[type void];',
+        '[type bool];',
+        '[type foo_t];',
+        '[variable foo_T];',
+        '[variable _t];'
+    )
+
+    var mode_cpp = CodeMirror.getMode({ indentUnit: 2 }, 'text/x-c++src')
+    function MTCPP(name) {
+        test.mode(name, mode_cpp, Array.prototype.slice.call(arguments, 1))
+    }
+
+    MTCPP('cpp14_literal', "[number 10'000];", "[number 0b10'000];", "[number 0x10'000];", "[string '100000'];")
+
+    MTCPP('ctor_dtor', '[def Foo::Foo]() {}', '[def Foo::~Foo]() {}')
+
+    MTCPP('cpp_underscores', '[builtin __FOO];', '[builtin _Complex];', '[builtin __aName];', '[variable _aName];')
+
+    var mode_objc = CodeMirror.getMode({ indentUnit: 2 }, 'text/x-objectivec')
+    function MTOBJC(name) {
+        test.mode(name, mode_objc, Array.prototype.slice.call(arguments, 1))
+    }
+
+    MTOBJC('objc_underscores', '[builtin __FOO];', '[builtin _Complex];', '[builtin __aName];', '[variable _aName];')
+
+    MTOBJC(
+        'objc_interface',
+        '[keyword @interface] [def foo] {',
+        '  [type int] [variable bar];',
+        '}',
+        '[keyword @property] ([keyword atomic], [keyword nullable]) [variable NSString][operator *] [variable a];',
+        '[keyword @property] ([keyword nonatomic], [keyword assign]) [type int] [variable b];',
+        '[operator -]([type instancetype])[variable initWithFoo]:([type int])[variable a] ' + '[builtin NS_DESIGNATED_INITIALIZER];',
+        '[keyword @end]'
+    )
+
+    MTOBJC(
+        'objc_implementation',
+        '[keyword @implementation] [def foo] {',
+        '  [type int] [variable bar];',
+        '}',
+        '[keyword @property] ([keyword readwrite]) [type SEL] [variable a];',
+        '[operator -]([type instancetype])[variable initWithFoo]:([type int])[variable a] {',
+        '  [keyword if](([keyword self] [operator =] [[[keyword super] [variable init] ]])) {}',
+        '  [keyword return] [keyword self];',
+        '}',
+        '[keyword @end]'
+    )
+
+    MTOBJC('objc_types', '[type int];', '[type foo_t];', '[variable foo_T];', '[type id];', '[type SEL];', '[type instancetype];', '[type Class];', '[type Protocol];', '[type BOOL];')
+
+    var mode_scala = CodeMirror.getMode({ indentUnit: 2 }, 'text/x-scala')
+    function MTSCALA(name) {
+        test.mode('scala_' + name, mode_scala, Array.prototype.slice.call(arguments, 1))
+    }
+    MTSCALA(
+        'nested_comments',
+        '[comment /*]',
+        '[comment But wait /* this is a nested comment */ for real]',
+        '[comment /**** let * me * show * you ****/]',
+        '[comment ///// let / me / show / you /////]',
+        '[comment */]'
+    )
+
+    var mode_java = CodeMirror.getMode({ indentUnit: 2 }, 'text/x-java')
+    function MTJAVA(name) {
+        test.mode('java_' + name, mode_java, Array.prototype.slice.call(arguments, 1))
+    }
+    MTJAVA(
+        'types',
+        '[type byte];',
+        '[type short];',
+        '[type int];',
+        '[type long];',
+        '[type float];',
+        '[type double];',
+        '[type boolean];',
+        '[type char];',
+        '[type void];',
+        '[type Boolean];',
+        '[type Byte];',
+        '[type Character];',
+        '[type Double];',
+        '[type Float];',
+        '[type Integer];',
+        '[type Long];',
+        '[type Number];',
+        '[type Object];',
+        '[type Short];',
+        '[type String];',
+        '[type StringBuffer];',
+        '[type StringBuilder];',
+        '[type Void];'
+    )
+})()

+ 880 - 0
libs/codemirror/mode/clojure/clojure.js

@@ -0,0 +1,880 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports === 'object' && typeof module === 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define === 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('clojure', function (options) {
+        var atoms = ['false', 'nil', 'true']
+        var specialForms = ['.', 'catch', 'def', 'do', 'if', 'monitor-enter', 'monitor-exit', 'new', 'quote', 'recur', 'set!', 'throw', 'try', 'var']
+        var coreSymbols = [
+            '*',
+            "*'",
+            '*1',
+            '*2',
+            '*3',
+            '*agent*',
+            '*allow-unresolved-vars*',
+            '*assert*',
+            '*clojure-version*',
+            '*command-line-args*',
+            '*compile-files*',
+            '*compile-path*',
+            '*compiler-options*',
+            '*data-readers*',
+            '*default-data-reader-fn*',
+            '*e',
+            '*err*',
+            '*file*',
+            '*flush-on-newline*',
+            '*fn-loader*',
+            '*in*',
+            '*math-context*',
+            '*ns*',
+            '*out*',
+            '*print-dup*',
+            '*print-length*',
+            '*print-level*',
+            '*print-meta*',
+            '*print-namespace-maps*',
+            '*print-readably*',
+            '*read-eval*',
+            '*reader-resolver*',
+            '*source-path*',
+            '*suppress-read*',
+            '*unchecked-math*',
+            '*use-context-classloader*',
+            '*verbose-defrecords*',
+            '*warn-on-reflection*',
+            '+',
+            "+'",
+            '-',
+            "-'",
+            '->',
+            '->>',
+            '->ArrayChunk',
+            '->Eduction',
+            '->Vec',
+            '->VecNode',
+            '->VecSeq',
+            '-cache-protocol-fn',
+            '-reset-methods',
+            '..',
+            '/',
+            '<',
+            '<=',
+            '=',
+            '==',
+            '>',
+            '>=',
+            'EMPTY-NODE',
+            'Inst',
+            'StackTraceElement->vec',
+            'Throwable->map',
+            'accessor',
+            'aclone',
+            'add-classpath',
+            'add-watch',
+            'agent',
+            'agent-error',
+            'agent-errors',
+            'aget',
+            'alength',
+            'alias',
+            'all-ns',
+            'alter',
+            'alter-meta!',
+            'alter-var-root',
+            'amap',
+            'ancestors',
+            'and',
+            'any?',
+            'apply',
+            'areduce',
+            'array-map',
+            'as->',
+            'aset',
+            'aset-boolean',
+            'aset-byte',
+            'aset-char',
+            'aset-double',
+            'aset-float',
+            'aset-int',
+            'aset-long',
+            'aset-short',
+            'assert',
+            'assoc',
+            'assoc!',
+            'assoc-in',
+            'associative?',
+            'atom',
+            'await',
+            'await-for',
+            'await1',
+            'bases',
+            'bean',
+            'bigdec',
+            'bigint',
+            'biginteger',
+            'binding',
+            'bit-and',
+            'bit-and-not',
+            'bit-clear',
+            'bit-flip',
+            'bit-not',
+            'bit-or',
+            'bit-set',
+            'bit-shift-left',
+            'bit-shift-right',
+            'bit-test',
+            'bit-xor',
+            'boolean',
+            'boolean-array',
+            'boolean?',
+            'booleans',
+            'bound-fn',
+            'bound-fn*',
+            'bound?',
+            'bounded-count',
+            'butlast',
+            'byte',
+            'byte-array',
+            'bytes',
+            'bytes?',
+            'case',
+            'cast',
+            'cat',
+            'char',
+            'char-array',
+            'char-escape-string',
+            'char-name-string',
+            'char?',
+            'chars',
+            'chunk',
+            'chunk-append',
+            'chunk-buffer',
+            'chunk-cons',
+            'chunk-first',
+            'chunk-next',
+            'chunk-rest',
+            'chunked-seq?',
+            'class',
+            'class?',
+            'clear-agent-errors',
+            'clojure-version',
+            'coll?',
+            'comment',
+            'commute',
+            'comp',
+            'comparator',
+            'compare',
+            'compare-and-set!',
+            'compile',
+            'complement',
+            'completing',
+            'concat',
+            'cond',
+            'cond->',
+            'cond->>',
+            'condp',
+            'conj',
+            'conj!',
+            'cons',
+            'constantly',
+            'construct-proxy',
+            'contains?',
+            'count',
+            'counted?',
+            'create-ns',
+            'create-struct',
+            'cycle',
+            'dec',
+            "dec'",
+            'decimal?',
+            'declare',
+            'dedupe',
+            'default-data-readers',
+            'definline',
+            'definterface',
+            'defmacro',
+            'defmethod',
+            'defmulti',
+            'defn',
+            'defn-',
+            'defonce',
+            'defprotocol',
+            'defrecord',
+            'defstruct',
+            'deftype',
+            'delay',
+            'delay?',
+            'deliver',
+            'denominator',
+            'deref',
+            'derive',
+            'descendants',
+            'destructure',
+            'disj',
+            'disj!',
+            'dissoc',
+            'dissoc!',
+            'distinct',
+            'distinct?',
+            'doall',
+            'dorun',
+            'doseq',
+            'dosync',
+            'dotimes',
+            'doto',
+            'double',
+            'double-array',
+            'double?',
+            'doubles',
+            'drop',
+            'drop-last',
+            'drop-while',
+            'eduction',
+            'empty',
+            'empty?',
+            'ensure',
+            'ensure-reduced',
+            'enumeration-seq',
+            'error-handler',
+            'error-mode',
+            'eval',
+            'even?',
+            'every-pred',
+            'every?',
+            'ex-data',
+            'ex-info',
+            'extend',
+            'extend-protocol',
+            'extend-type',
+            'extenders',
+            'extends?',
+            'false?',
+            'ffirst',
+            'file-seq',
+            'filter',
+            'filterv',
+            'find',
+            'find-keyword',
+            'find-ns',
+            'find-protocol-impl',
+            'find-protocol-method',
+            'find-var',
+            'first',
+            'flatten',
+            'float',
+            'float-array',
+            'float?',
+            'floats',
+            'flush',
+            'fn',
+            'fn?',
+            'fnext',
+            'fnil',
+            'for',
+            'force',
+            'format',
+            'frequencies',
+            'future',
+            'future-call',
+            'future-cancel',
+            'future-cancelled?',
+            'future-done?',
+            'future?',
+            'gen-class',
+            'gen-interface',
+            'gensym',
+            'get',
+            'get-in',
+            'get-method',
+            'get-proxy-class',
+            'get-thread-bindings',
+            'get-validator',
+            'group-by',
+            'halt-when',
+            'hash',
+            'hash-combine',
+            'hash-map',
+            'hash-ordered-coll',
+            'hash-set',
+            'hash-unordered-coll',
+            'ident?',
+            'identical?',
+            'identity',
+            'if-let',
+            'if-not',
+            'if-some',
+            'ifn?',
+            'import',
+            'in-ns',
+            'inc',
+            "inc'",
+            'indexed?',
+            'init-proxy',
+            'inst-ms',
+            'inst-ms*',
+            'inst?',
+            'instance?',
+            'int',
+            'int-array',
+            'int?',
+            'integer?',
+            'interleave',
+            'intern',
+            'interpose',
+            'into',
+            'into-array',
+            'ints',
+            'io!',
+            'isa?',
+            'iterate',
+            'iterator-seq',
+            'juxt',
+            'keep',
+            'keep-indexed',
+            'key',
+            'keys',
+            'keyword',
+            'keyword?',
+            'last',
+            'lazy-cat',
+            'lazy-seq',
+            'let',
+            'letfn',
+            'line-seq',
+            'list',
+            'list*',
+            'list?',
+            'load',
+            'load-file',
+            'load-reader',
+            'load-string',
+            'loaded-libs',
+            'locking',
+            'long',
+            'long-array',
+            'longs',
+            'loop',
+            'macroexpand',
+            'macroexpand-1',
+            'make-array',
+            'make-hierarchy',
+            'map',
+            'map-entry?',
+            'map-indexed',
+            'map?',
+            'mapcat',
+            'mapv',
+            'max',
+            'max-key',
+            'memfn',
+            'memoize',
+            'merge',
+            'merge-with',
+            'meta',
+            'method-sig',
+            'methods',
+            'min',
+            'min-key',
+            'mix-collection-hash',
+            'mod',
+            'munge',
+            'name',
+            'namespace',
+            'namespace-munge',
+            'nat-int?',
+            'neg-int?',
+            'neg?',
+            'newline',
+            'next',
+            'nfirst',
+            'nil?',
+            'nnext',
+            'not',
+            'not-any?',
+            'not-empty',
+            'not-every?',
+            'not=',
+            'ns',
+            'ns-aliases',
+            'ns-imports',
+            'ns-interns',
+            'ns-map',
+            'ns-name',
+            'ns-publics',
+            'ns-refers',
+            'ns-resolve',
+            'ns-unalias',
+            'ns-unmap',
+            'nth',
+            'nthnext',
+            'nthrest',
+            'num',
+            'number?',
+            'numerator',
+            'object-array',
+            'odd?',
+            'or',
+            'parents',
+            'partial',
+            'partition',
+            'partition-all',
+            'partition-by',
+            'pcalls',
+            'peek',
+            'persistent!',
+            'pmap',
+            'pop',
+            'pop!',
+            'pop-thread-bindings',
+            'pos-int?',
+            'pos?',
+            'pr',
+            'pr-str',
+            'prefer-method',
+            'prefers',
+            'primitives-classnames',
+            'print',
+            'print-ctor',
+            'print-dup',
+            'print-method',
+            'print-simple',
+            'print-str',
+            'printf',
+            'println',
+            'println-str',
+            'prn',
+            'prn-str',
+            'promise',
+            'proxy',
+            'proxy-call-with-super',
+            'proxy-mappings',
+            'proxy-name',
+            'proxy-super',
+            'push-thread-bindings',
+            'pvalues',
+            'qualified-ident?',
+            'qualified-keyword?',
+            'qualified-symbol?',
+            'quot',
+            'rand',
+            'rand-int',
+            'rand-nth',
+            'random-sample',
+            'range',
+            'ratio?',
+            'rational?',
+            'rationalize',
+            're-find',
+            're-groups',
+            're-matcher',
+            're-matches',
+            're-pattern',
+            're-seq',
+            'read',
+            'read-line',
+            'read-string',
+            'reader-conditional',
+            'reader-conditional?',
+            'realized?',
+            'record?',
+            'reduce',
+            'reduce-kv',
+            'reduced',
+            'reduced?',
+            'reductions',
+            'ref',
+            'ref-history-count',
+            'ref-max-history',
+            'ref-min-history',
+            'ref-set',
+            'refer',
+            'refer-clojure',
+            'reify',
+            'release-pending-sends',
+            'rem',
+            'remove',
+            'remove-all-methods',
+            'remove-method',
+            'remove-ns',
+            'remove-watch',
+            'repeat',
+            'repeatedly',
+            'replace',
+            'replicate',
+            'require',
+            'reset!',
+            'reset-meta!',
+            'reset-vals!',
+            'resolve',
+            'rest',
+            'restart-agent',
+            'resultset-seq',
+            'reverse',
+            'reversible?',
+            'rseq',
+            'rsubseq',
+            'run!',
+            'satisfies?',
+            'second',
+            'select-keys',
+            'send',
+            'send-off',
+            'send-via',
+            'seq',
+            'seq?',
+            'seqable?',
+            'seque',
+            'sequence',
+            'sequential?',
+            'set',
+            'set-agent-send-executor!',
+            'set-agent-send-off-executor!',
+            'set-error-handler!',
+            'set-error-mode!',
+            'set-validator!',
+            'set?',
+            'short',
+            'short-array',
+            'shorts',
+            'shuffle',
+            'shutdown-agents',
+            'simple-ident?',
+            'simple-keyword?',
+            'simple-symbol?',
+            'slurp',
+            'some',
+            'some->',
+            'some->>',
+            'some-fn',
+            'some?',
+            'sort',
+            'sort-by',
+            'sorted-map',
+            'sorted-map-by',
+            'sorted-set',
+            'sorted-set-by',
+            'sorted?',
+            'special-symbol?',
+            'spit',
+            'split-at',
+            'split-with',
+            'str',
+            'string?',
+            'struct',
+            'struct-map',
+            'subs',
+            'subseq',
+            'subvec',
+            'supers',
+            'swap!',
+            'swap-vals!',
+            'symbol',
+            'symbol?',
+            'sync',
+            'tagged-literal',
+            'tagged-literal?',
+            'take',
+            'take-last',
+            'take-nth',
+            'take-while',
+            'test',
+            'the-ns',
+            'thread-bound?',
+            'time',
+            'to-array',
+            'to-array-2d',
+            'trampoline',
+            'transduce',
+            'transient',
+            'tree-seq',
+            'true?',
+            'type',
+            'unchecked-add',
+            'unchecked-add-int',
+            'unchecked-byte',
+            'unchecked-char',
+            'unchecked-dec',
+            'unchecked-dec-int',
+            'unchecked-divide-int',
+            'unchecked-double',
+            'unchecked-float',
+            'unchecked-inc',
+            'unchecked-inc-int',
+            'unchecked-int',
+            'unchecked-long',
+            'unchecked-multiply',
+            'unchecked-multiply-int',
+            'unchecked-negate',
+            'unchecked-negate-int',
+            'unchecked-remainder-int',
+            'unchecked-short',
+            'unchecked-subtract',
+            'unchecked-subtract-int',
+            'underive',
+            'unquote',
+            'unquote-splicing',
+            'unreduced',
+            'unsigned-bit-shift-right',
+            'update',
+            'update-in',
+            'update-proxy',
+            'uri?',
+            'use',
+            'uuid?',
+            'val',
+            'vals',
+            'var-get',
+            'var-set',
+            'var?',
+            'vary-meta',
+            'vec',
+            'vector',
+            'vector-of',
+            'vector?',
+            'volatile!',
+            'volatile?',
+            'vreset!',
+            'vswap!',
+            'when',
+            'when-first',
+            'when-let',
+            'when-not',
+            'when-some',
+            'while',
+            'with-bindings',
+            'with-bindings*',
+            'with-in-str',
+            'with-loading-context',
+            'with-local-vars',
+            'with-meta',
+            'with-open',
+            'with-out-str',
+            'with-precision',
+            'with-redefs',
+            'with-redefs-fn',
+            'xml-seq',
+            'zero?',
+            'zipmap',
+        ]
+        var haveBodyParameter = [
+            '->',
+            '->>',
+            'as->',
+            'binding',
+            'bound-fn',
+            'case',
+            'catch',
+            'comment',
+            'cond',
+            'cond->',
+            'cond->>',
+            'condp',
+            'def',
+            'definterface',
+            'defmethod',
+            'defn',
+            'defmacro',
+            'defprotocol',
+            'defrecord',
+            'defstruct',
+            'deftype',
+            'do',
+            'doseq',
+            'dotimes',
+            'doto',
+            'extend',
+            'extend-protocol',
+            'extend-type',
+            'fn',
+            'for',
+            'future',
+            'if',
+            'if-let',
+            'if-not',
+            'if-some',
+            'let',
+            'letfn',
+            'locking',
+            'loop',
+            'ns',
+            'proxy',
+            'reify',
+            'struct-map',
+            'some->',
+            'some->>',
+            'try',
+            'when',
+            'when-first',
+            'when-let',
+            'when-not',
+            'when-some',
+            'while',
+            'with-bindings',
+            'with-bindings*',
+            'with-in-str',
+            'with-loading-context',
+            'with-local-vars',
+            'with-meta',
+            'with-open',
+            'with-out-str',
+            'with-precision',
+            'with-redefs',
+            'with-redefs-fn',
+        ]
+
+        CodeMirror.registerHelper('hintWords', 'clojure', [].concat(atoms, specialForms, coreSymbols))
+
+        var atom = createLookupMap(atoms)
+        var specialForm = createLookupMap(specialForms)
+        var coreSymbol = createLookupMap(coreSymbols)
+        var hasBodyParameter = createLookupMap(haveBodyParameter)
+        var delimiter = /^(?:[\\\[\]\s"(),;@^`{}~]|$)/
+        var numberLiteral = /^(?:[+\-]?\d+(?:(?:N|(?:[eE][+\-]?\d+))|(?:\.?\d*(?:M|(?:[eE][+\-]?\d+))?)|\/\d+|[xX][0-9a-fA-F]+|r[0-9a-zA-Z]+)?(?=[\\\[\]\s"#'(),;@^`{}~]|$))/
+        var characterLiteral = /^(?:\\(?:backspace|formfeed|newline|return|space|tab|o[0-7]{3}|u[0-9A-Fa-f]{4}|x[0-9A-Fa-f]{4}|.)?(?=[\\\[\]\s"(),;@^`{}~]|$))/
+
+        // simple-namespace := /^[^\\\/\[\]\d\s"#'(),;@^`{}~.][^\\\[\]\s"(),;@^`{}~.\/]*/
+        // simple-symbol    := /^(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)/
+        // qualified-symbol := (<simple-namespace>(<.><simple-namespace>)*</>)?<simple-symbol>
+        var qualifiedSymbol =
+            /^(?:(?:[^\\\/\[\]\d\s"#'(),;@^`{}~.][^\\\[\]\s"(),;@^`{}~.\/]*(?:\.[^\\\/\[\]\d\s"#'(),;@^`{}~.][^\\\[\]\s"(),;@^`{}~.\/]*)*\/)?(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*(?=[\\\[\]\s"(),;@^`{}~]|$))/
+
+        function base(stream, state) {
+            if (stream.eatSpace() || stream.eat(',')) return ['space', null]
+            if (stream.match(numberLiteral)) return [null, 'number']
+            if (stream.match(characterLiteral)) return [null, 'string-2']
+            if (stream.eat(/^"/)) return (state.tokenize = inString)(stream, state)
+            if (stream.eat(/^[(\[{]/)) return ['open', 'bracket']
+            if (stream.eat(/^[)\]}]/)) return ['close', 'bracket']
+            if (stream.eat(/^;/)) {
+                stream.skipToEnd()
+                return ['space', 'comment']
+            }
+            if (stream.eat(/^[#'@^`~]/)) return [null, 'meta']
+
+            var matches = stream.match(qualifiedSymbol)
+            var symbol = matches && matches[0]
+
+            if (!symbol) {
+                // advance stream by at least one character so we don't get stuck.
+                stream.next()
+                stream.eatWhile(function (c) {
+                    return !is(c, delimiter)
+                })
+                return [null, 'error']
+            }
+
+            if (symbol === 'comment' && state.lastToken === '(') return (state.tokenize = inComment)(stream, state)
+            if (is(symbol, atom) || symbol.charAt(0) === ':') return ['symbol', 'atom']
+            if (is(symbol, specialForm) || is(symbol, coreSymbol)) return ['symbol', 'keyword']
+            if (state.lastToken === '(') return ['symbol', 'builtin'] // other operator
+
+            return ['symbol', 'variable']
+        }
+
+        function inString(stream, state) {
+            var escaped = false,
+                next
+
+            while ((next = stream.next())) {
+                if (next === '"' && !escaped) {
+                    state.tokenize = base
+                    break
+                }
+                escaped = !escaped && next === '\\'
+            }
+
+            return [null, 'string']
+        }
+
+        function inComment(stream, state) {
+            var parenthesisCount = 1
+            var next
+
+            while ((next = stream.next())) {
+                if (next === ')') parenthesisCount--
+                if (next === '(') parenthesisCount++
+                if (parenthesisCount === 0) {
+                    stream.backUp(1)
+                    state.tokenize = base
+                    break
+                }
+            }
+
+            return ['space', 'comment']
+        }
+
+        function createLookupMap(words) {
+            var obj = {}
+
+            for (var i = 0; i < words.length; ++i) obj[words[i]] = true
+
+            return obj
+        }
+
+        function is(value, test) {
+            if (test instanceof RegExp) return test.test(value)
+            if (test instanceof Object) return test.propertyIsEnumerable(value)
+        }
+
+        return {
+            startState: function () {
+                return {
+                    ctx: { prev: null, start: 0, indentTo: 0 },
+                    lastToken: null,
+                    tokenize: base,
+                }
+            },
+
+            token: function (stream, state) {
+                if (stream.sol() && typeof state.ctx.indentTo !== 'number') state.ctx.indentTo = state.ctx.start + 1
+
+                var typeStylePair = state.tokenize(stream, state)
+                var type = typeStylePair[0]
+                var style = typeStylePair[1]
+                var current = stream.current()
+
+                if (type !== 'space') {
+                    if (state.lastToken === '(' && state.ctx.indentTo === null) {
+                        if (type === 'symbol' && is(current, hasBodyParameter)) state.ctx.indentTo = state.ctx.start + options.indentUnit
+                        else state.ctx.indentTo = 'next'
+                    } else if (state.ctx.indentTo === 'next') {
+                        state.ctx.indentTo = stream.column()
+                    }
+
+                    state.lastToken = current
+                }
+
+                if (type === 'open') state.ctx = { prev: state.ctx, start: stream.column(), indentTo: null }
+                else if (type === 'close') state.ctx = state.ctx.prev || state.ctx
+
+                return style
+            },
+
+            indent: function (state) {
+                var i = state.ctx.indentTo
+
+                return typeof i === 'number' ? i : state.ctx.start + 1
+            },
+
+            closeBrackets: { pairs: '()[]{}""' },
+            lineComment: ';;',
+        }
+    })
+
+    CodeMirror.defineMIME('text/x-clojure', 'clojure')
+    CodeMirror.defineMIME('text/x-clojurescript', 'clojure')
+    CodeMirror.defineMIME('application/edn', 'clojure')
+})

+ 95 - 0
libs/codemirror/mode/clojure/index.html

@@ -0,0 +1,95 @@
+<!doctype html>
+
+<title>CodeMirror: Clojure mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/closebrackets.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<script src="clojure.js"></script>
+<style>.CodeMirror {background: #f8f8f8;}</style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">Clojure</a>
+  </ul>
+</div>
+
+<article>
+<h2>Clojure mode</h2>
+<form><textarea id="code" name="code">
+(ns game-of-life
+  "Conway's Game of Life, based on the work of
+  Christophe Grand (http://clj-me.cgrand.net/2011/08/19/conways-game-of-life)
+  and Laurent Petit (https://gist.github.com/1200343).")
+
+;;; Core game of life's algorithm functions
+
+(defn neighbors
+  "Given a cell's coordinates `[x y]`, returns the coordinates of its
+  neighbors."
+  [[x y]]
+  (for [dx [-1 0 1]
+        dy (if (zero? dx)
+             [-1 1]
+             [-1 0 1])]
+    [(+ dx x) (+ dy y)]))
+
+(defn step
+  "Given a set of living `cells`, computes the new set of living cells."
+  [cells]
+  (set (for [[cell n] (frequencies (mapcat neighbors cells))
+             :when (or (= n 3)
+                       (and (= n 2)
+                            (cells cell)))]
+         cell)))
+
+;;; Utility methods for displaying game on a text terminal
+
+(defn print-grid
+  "Prints a `grid` of `w` columns and `h` rows, on *out*, representing a
+  step in the game."
+  [grid w h]
+  (doseq [x (range (inc w))
+          y (range (inc h))]
+    (when (= y 0) (println))
+    (print (if (grid [x y])
+             "[X]"
+             " . "))))
+
+(defn print-grids
+  "Prints a sequence of `grids` of `w` columns and `h` rows on *out*,
+  representing several steps."
+  [grids w h]
+  (doseq [grid grids]
+    (print-grid grid w h)
+    (println)))
+
+;;; Launches an example grid
+
+(def grid
+  "`grid` represents the initial set of living cells"
+  #{[2 1] [2 2] [2 3]})
+
+(print-grids (take 3 (iterate step grid)) 5 5)</textarea></form>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+          autoCloseBrackets: true,
+          lineNumbers: true,
+          matchBrackets: true,
+          mode: 'text/x-clojure'
+      });
+    </script>
+
+    <p><strong>MIME types defined:</strong> <code>text/x-clojure</code>.</p>
+
+  </article>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 999 - 0
libs/codemirror/mode/clojure/test.js


+ 95 - 0
libs/codemirror/mode/cmake/cmake.js

@@ -0,0 +1,95 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object') mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd) define(['../../lib/codemirror'], mod)
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('cmake', function () {
+        var variable_regex = /({)?[a-zA-Z0-9_]+(})?/
+
+        function tokenString(stream, state) {
+            var current,
+                prev,
+                found_var = false
+            while (!stream.eol() && (current = stream.next()) != state.pending) {
+                if (current === '$' && prev != '\\' && state.pending == '"') {
+                    found_var = true
+                    break
+                }
+                prev = current
+            }
+            if (found_var) {
+                stream.backUp(1)
+            }
+            if (current == state.pending) {
+                state.continueString = false
+            } else {
+                state.continueString = true
+            }
+            return 'string'
+        }
+
+        function tokenize(stream, state) {
+            var ch = stream.next()
+
+            // Have we found a variable?
+            if (ch === '$') {
+                if (stream.match(variable_regex)) {
+                    return 'variable-2'
+                }
+                return 'variable'
+            }
+            // Should we still be looking for the end of a string?
+            if (state.continueString) {
+                // If so, go through the loop again
+                stream.backUp(1)
+                return tokenString(stream, state)
+            }
+            // Do we just have a function on our hands?
+            // In 'cmake_minimum_required (VERSION 2.8.8)', 'cmake_minimum_required' is matched
+            if (stream.match(/(\s+)?\w+\(/) || stream.match(/(\s+)?\w+\ \(/)) {
+                stream.backUp(1)
+                return 'def'
+            }
+            if (ch == '#') {
+                stream.skipToEnd()
+                return 'comment'
+            }
+            // Have we found a string?
+            if (ch == "'" || ch == '"') {
+                // Store the type (single or double)
+                state.pending = ch
+                // Perform the looping function to find the end
+                return tokenString(stream, state)
+            }
+            if (ch == '(' || ch == ')') {
+                return 'bracket'
+            }
+            if (ch.match(/[0-9]/)) {
+                return 'number'
+            }
+            stream.eatWhile(/[\w-]/)
+            return null
+        }
+        return {
+            startState: function () {
+                var state = {}
+                state.inDefinition = false
+                state.inInclude = false
+                state.continueString = false
+                state.pending = false
+                return state
+            },
+            token: function (stream, state) {
+                if (stream.eatSpace()) return null
+                return tokenize(stream, state)
+            },
+        }
+    })
+
+    CodeMirror.defineMIME('text/x-cmake', 'cmake')
+})

+ 129 - 0
libs/codemirror/mode/cmake/index.html

@@ -0,0 +1,129 @@
+<!doctype html>
+
+<title>CodeMirror: CMake mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<script src="cmake.js"></script>
+<style>
+      .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
+      .cm-s-default span.cm-arrow { color: red; }
+    </style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">CMake</a>
+  </ul>
+</div>
+
+<article>
+<h2>CMake mode</h2>
+<form><textarea id="code" name="code">
+# vim: syntax=cmake
+if(NOT CMAKE_BUILD_TYPE)
+    # default to Release build for GCC builds
+    set(CMAKE_BUILD_TYPE Release CACHE STRING
+        "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel."
+        FORCE)
+endif()
+message(STATUS "cmake version ${CMAKE_VERSION}")
+if(POLICY CMP0025)
+    cmake_policy(SET CMP0025 OLD) # report Apple's Clang as just Clang
+endif()
+if(POLICY CMP0042)
+    cmake_policy(SET CMP0042 NEW) # MACOSX_RPATH
+endif()
+
+project (x265)
+cmake_minimum_required (VERSION 2.8.8) # OBJECT libraries require 2.8.8
+include(CheckIncludeFiles)
+include(CheckFunctionExists)
+include(CheckSymbolExists)
+include(CheckCXXCompilerFlag)
+
+# X265_BUILD must be incremented each time the public API is changed
+set(X265_BUILD 48)
+configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
+               "${PROJECT_BINARY_DIR}/x265.def")
+configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
+               "${PROJECT_BINARY_DIR}/x265_config.h")
+
+SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}")
+
+# System architecture detection
+string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" SYSPROC)
+set(X86_ALIASES x86 i386 i686 x86_64 amd64)
+list(FIND X86_ALIASES "${SYSPROC}" X86MATCH)
+if("${SYSPROC}" STREQUAL "" OR X86MATCH GREATER "-1")
+    message(STATUS "Detected x86 target processor")
+    set(X86 1)
+    add_definitions(-DX265_ARCH_X86=1)
+    if("${CMAKE_SIZEOF_VOID_P}" MATCHES 8)
+        set(X64 1)
+        add_definitions(-DX86_64=1)
+    endif()
+elseif(${SYSPROC} STREQUAL "armv6l")
+    message(STATUS "Detected ARM target processor")
+    set(ARM 1)
+    add_definitions(-DX265_ARCH_ARM=1 -DHAVE_ARMV6=1)
+else()
+    message(STATUS "CMAKE_SYSTEM_PROCESSOR value `${CMAKE_SYSTEM_PROCESSOR}` is unknown")
+    message(STATUS "Please add this value near ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}")
+endif()
+
+if(UNIX)
+    list(APPEND PLATFORM_LIBS pthread)
+    find_library(LIBRT rt)
+    if(LIBRT)
+        list(APPEND PLATFORM_LIBS rt)
+    endif()
+    find_package(Numa)
+    if(NUMA_FOUND)
+        list(APPEND CMAKE_REQUIRED_LIBRARIES ${NUMA_LIBRARY})
+        check_symbol_exists(numa_node_of_cpu numa.h NUMA_V2)
+        if(NUMA_V2)
+            add_definitions(-DHAVE_LIBNUMA)
+            message(STATUS "libnuma found, building with support for NUMA nodes")
+            list(APPEND PLATFORM_LIBS ${NUMA_LIBRARY})
+            link_directories(${NUMA_LIBRARY_DIR})
+            include_directories(${NUMA_INCLUDE_DIR})
+        endif()
+    endif()
+    mark_as_advanced(LIBRT NUMA_FOUND)
+endif(UNIX)
+
+if(X64 AND NOT WIN32)
+    option(ENABLE_PIC "Enable Position Independent Code" ON)
+else()
+    option(ENABLE_PIC "Enable Position Independent Code" OFF)
+endif(X64 AND NOT WIN32)
+
+# Compiler detection
+if(CMAKE_GENERATOR STREQUAL "Xcode")
+  set(XCODE 1)
+endif()
+if (APPLE)
+  add_definitions(-DMACOS)
+endif()
+</textarea></form>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        mode: "text/x-cmake",
+        matchBrackets: true,
+        indentUnit: 4
+      });
+    </script>
+
+    <p><strong>MIME types defined:</strong> <code>text/x-cmake</code>.</p>
+
+  </article>

+ 266 - 0
libs/codemirror/mode/cobol/cobol.js

@@ -0,0 +1,266 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/**
+ * Author: Gautam Mehta
+ * Branched from CodeMirror's Scheme mode
+ */
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('cobol', function () {
+        var BUILTIN = 'builtin',
+            COMMENT = 'comment',
+            STRING = 'string',
+            ATOM = 'atom',
+            NUMBER = 'number',
+            KEYWORD = 'keyword',
+            MODTAG = 'header',
+            COBOLLINENUM = 'def',
+            PERIOD = 'link'
+        function makeKeywords(str) {
+            var obj = {},
+                words = str.split(' ')
+            for (var i = 0; i < words.length; ++i) obj[words[i]] = true
+            return obj
+        }
+        var atoms = makeKeywords('TRUE FALSE ZEROES ZEROS ZERO SPACES SPACE LOW-VALUE LOW-VALUES ')
+        var keywords = makeKeywords(
+            'ACCEPT ACCESS ACQUIRE ADD ADDRESS ' +
+                'ADVANCING AFTER ALIAS ALL ALPHABET ' +
+                'ALPHABETIC ALPHABETIC-LOWER ALPHABETIC-UPPER ALPHANUMERIC ALPHANUMERIC-EDITED ' +
+                'ALSO ALTER ALTERNATE AND ANY ' +
+                'ARE AREA AREAS ARITHMETIC ASCENDING ' +
+                'ASSIGN AT ATTRIBUTE AUTHOR AUTO ' +
+                'AUTO-SKIP AUTOMATIC B-AND B-EXOR B-LESS ' +
+                'B-NOT B-OR BACKGROUND-COLOR BACKGROUND-COLOUR BEEP ' +
+                'BEFORE BELL BINARY BIT BITS ' +
+                'BLANK BLINK BLOCK BOOLEAN BOTTOM ' +
+                'BY CALL CANCEL CD CF ' +
+                'CH CHARACTER CHARACTERS CLASS CLOCK-UNITS ' +
+                'CLOSE COBOL CODE CODE-SET COL ' +
+                'COLLATING COLUMN COMMA COMMIT COMMITMENT ' +
+                'COMMON COMMUNICATION COMP COMP-0 COMP-1 ' +
+                'COMP-2 COMP-3 COMP-4 COMP-5 COMP-6 ' +
+                'COMP-7 COMP-8 COMP-9 COMPUTATIONAL COMPUTATIONAL-0 ' +
+                'COMPUTATIONAL-1 COMPUTATIONAL-2 COMPUTATIONAL-3 COMPUTATIONAL-4 COMPUTATIONAL-5 ' +
+                'COMPUTATIONAL-6 COMPUTATIONAL-7 COMPUTATIONAL-8 COMPUTATIONAL-9 COMPUTE ' +
+                'CONFIGURATION CONNECT CONSOLE CONTAINED CONTAINS ' +
+                'CONTENT CONTINUE CONTROL CONTROL-AREA CONTROLS ' +
+                'CONVERTING COPY CORR CORRESPONDING COUNT ' +
+                'CRT CRT-UNDER CURRENCY CURRENT CURSOR ' +
+                'DATA DATE DATE-COMPILED DATE-WRITTEN DAY ' +
+                'DAY-OF-WEEK DB DB-ACCESS-CONTROL-KEY DB-DATA-NAME DB-EXCEPTION ' +
+                'DB-FORMAT-NAME DB-RECORD-NAME DB-SET-NAME DB-STATUS DBCS ' +
+                'DBCS-EDITED DE DEBUG-CONTENTS DEBUG-ITEM DEBUG-LINE ' +
+                'DEBUG-NAME DEBUG-SUB-1 DEBUG-SUB-2 DEBUG-SUB-3 DEBUGGING ' +
+                'DECIMAL-POINT DECLARATIVES DEFAULT DELETE DELIMITED ' +
+                'DELIMITER DEPENDING DESCENDING DESCRIBED DESTINATION ' +
+                'DETAIL DISABLE DISCONNECT DISPLAY DISPLAY-1 ' +
+                'DISPLAY-2 DISPLAY-3 DISPLAY-4 DISPLAY-5 DISPLAY-6 ' +
+                'DISPLAY-7 DISPLAY-8 DISPLAY-9 DIVIDE DIVISION ' +
+                'DOWN DROP DUPLICATE DUPLICATES DYNAMIC ' +
+                'EBCDIC EGI EJECT ELSE EMI ' +
+                'EMPTY EMPTY-CHECK ENABLE END END. END-ACCEPT END-ACCEPT. ' +
+                'END-ADD END-CALL END-COMPUTE END-DELETE END-DISPLAY ' +
+                'END-DIVIDE END-EVALUATE END-IF END-INVOKE END-MULTIPLY ' +
+                'END-OF-PAGE END-PERFORM END-READ END-RECEIVE END-RETURN ' +
+                'END-REWRITE END-SEARCH END-START END-STRING END-SUBTRACT ' +
+                'END-UNSTRING END-WRITE END-XML ENTER ENTRY ' +
+                'ENVIRONMENT EOP EQUAL EQUALS ERASE ' +
+                'ERROR ESI EVALUATE EVERY EXCEEDS ' +
+                'EXCEPTION EXCLUSIVE EXIT EXTEND EXTERNAL ' +
+                'EXTERNALLY-DESCRIBED-KEY FD FETCH FILE FILE-CONTROL ' +
+                'FILE-STREAM FILES FILLER FINAL FIND ' +
+                'FINISH FIRST FOOTING FOR FOREGROUND-COLOR ' +
+                'FOREGROUND-COLOUR FORMAT FREE FROM FULL ' +
+                'FUNCTION GENERATE GET GIVING GLOBAL ' +
+                'GO GOBACK GREATER GROUP HEADING ' +
+                'HIGH-VALUE HIGH-VALUES HIGHLIGHT I-O I-O-CONTROL ' +
+                'ID IDENTIFICATION IF IN INDEX ' +
+                'INDEX-1 INDEX-2 INDEX-3 INDEX-4 INDEX-5 ' +
+                'INDEX-6 INDEX-7 INDEX-8 INDEX-9 INDEXED ' +
+                'INDIC INDICATE INDICATOR INDICATORS INITIAL ' +
+                'INITIALIZE INITIATE INPUT INPUT-OUTPUT INSPECT ' +
+                'INSTALLATION INTO INVALID INVOKE IS ' +
+                'JUST JUSTIFIED KANJI KEEP KEY ' +
+                'LABEL LAST LD LEADING LEFT ' +
+                'LEFT-JUSTIFY LENGTH LENGTH-CHECK LESS LIBRARY ' +
+                'LIKE LIMIT LIMITS LINAGE LINAGE-COUNTER ' +
+                'LINE LINE-COUNTER LINES LINKAGE LOCAL-STORAGE ' +
+                'LOCALE LOCALLY LOCK ' +
+                'MEMBER MEMORY MERGE MESSAGE METACLASS ' +
+                'MODE MODIFIED MODIFY MODULES MOVE ' +
+                'MULTIPLE MULTIPLY NATIONAL NATIVE NEGATIVE ' +
+                'NEXT NO NO-ECHO NONE NOT ' +
+                'NULL NULL-KEY-MAP NULL-MAP NULLS NUMBER ' +
+                'NUMERIC NUMERIC-EDITED OBJECT OBJECT-COMPUTER OCCURS ' +
+                'OF OFF OMITTED ON ONLY ' +
+                'OPEN OPTIONAL OR ORDER ORGANIZATION ' +
+                'OTHER OUTPUT OVERFLOW OWNER PACKED-DECIMAL ' +
+                'PADDING PAGE PAGE-COUNTER PARSE PERFORM ' +
+                'PF PH PIC PICTURE PLUS ' +
+                'POINTER POSITION POSITIVE PREFIX PRESENT ' +
+                'PRINTING PRIOR PROCEDURE PROCEDURE-POINTER PROCEDURES ' +
+                'PROCEED PROCESS PROCESSING PROGRAM PROGRAM-ID ' +
+                'PROMPT PROTECTED PURGE QUEUE QUOTE ' +
+                'QUOTES RANDOM RD READ READY ' +
+                'REALM RECEIVE RECONNECT RECORD RECORD-NAME ' +
+                'RECORDS RECURSIVE REDEFINES REEL REFERENCE ' +
+                'REFERENCE-MONITOR REFERENCES RELATION RELATIVE RELEASE ' +
+                'REMAINDER REMOVAL RENAMES REPEATED REPLACE ' +
+                'REPLACING REPORT REPORTING REPORTS REPOSITORY ' +
+                'REQUIRED RERUN RESERVE RESET RETAINING ' +
+                'RETRIEVAL RETURN RETURN-CODE RETURNING REVERSE-VIDEO ' +
+                'REVERSED REWIND REWRITE RF RH ' +
+                'RIGHT RIGHT-JUSTIFY ROLLBACK ROLLING ROUNDED ' +
+                'RUN SAME SCREEN SD SEARCH ' +
+                'SECTION SECURE SECURITY SEGMENT SEGMENT-LIMIT ' +
+                'SELECT SEND SENTENCE SEPARATE SEQUENCE ' +
+                'SEQUENTIAL SET SHARED SIGN SIZE ' +
+                'SKIP1 SKIP2 SKIP3 SORT SORT-MERGE ' +
+                'SORT-RETURN SOURCE SOURCE-COMPUTER SPACE-FILL ' +
+                'SPECIAL-NAMES STANDARD STANDARD-1 STANDARD-2 ' +
+                'START STARTING STATUS STOP STORE ' +
+                'STRING SUB-QUEUE-1 SUB-QUEUE-2 SUB-QUEUE-3 SUB-SCHEMA ' +
+                'SUBFILE SUBSTITUTE SUBTRACT SUM SUPPRESS ' +
+                'SYMBOLIC SYNC SYNCHRONIZED SYSIN SYSOUT ' +
+                'TABLE TALLYING TAPE TENANT TERMINAL ' +
+                'TERMINATE TEST TEXT THAN THEN ' +
+                'THROUGH THRU TIME TIMES TITLE ' +
+                'TO TOP TRAILING TRAILING-SIGN TRANSACTION ' +
+                'TYPE TYPEDEF UNDERLINE UNEQUAL UNIT ' +
+                'UNSTRING UNTIL UP UPDATE UPON ' +
+                'USAGE USAGE-MODE USE USING VALID ' +
+                'VALIDATE VALUE VALUES VARYING VLR ' +
+                'WAIT WHEN WHEN-COMPILED WITH WITHIN ' +
+                'WORDS WORKING-STORAGE WRITE XML XML-CODE ' +
+                'XML-EVENT XML-NTEXT XML-TEXT ZERO ZERO-FILL '
+        )
+
+        var builtins = makeKeywords('- * ** / + < <= = > >= ')
+        var tests = {
+            digit: /\d/,
+            digit_or_colon: /[\d:]/,
+            hex: /[0-9a-f]/i,
+            sign: /[+-]/,
+            exponent: /e/i,
+            keyword_char: /[^\s\(\[\;\)\]]/,
+            symbol: /[\w*+\-]/,
+        }
+        function isNumber(ch, stream) {
+            // hex
+            if (ch === '0' && stream.eat(/x/i)) {
+                stream.eatWhile(tests.hex)
+                return true
+            }
+            // leading sign
+            if ((ch == '+' || ch == '-') && tests.digit.test(stream.peek())) {
+                stream.eat(tests.sign)
+                ch = stream.next()
+            }
+            if (tests.digit.test(ch)) {
+                stream.eat(ch)
+                stream.eatWhile(tests.digit)
+                if ('.' == stream.peek()) {
+                    stream.eat('.')
+                    stream.eatWhile(tests.digit)
+                }
+                if (stream.eat(tests.exponent)) {
+                    stream.eat(tests.sign)
+                    stream.eatWhile(tests.digit)
+                }
+                return true
+            }
+            return false
+        }
+        return {
+            startState: function () {
+                return {
+                    indentStack: null,
+                    indentation: 0,
+                    mode: false,
+                }
+            },
+            token: function (stream, state) {
+                if (state.indentStack == null && stream.sol()) {
+                    // update indentation, but only if indentStack is empty
+                    state.indentation = 6 //stream.indentation();
+                }
+                // skip spaces
+                if (stream.eatSpace()) {
+                    return null
+                }
+                var returnType = null
+                switch (state.mode) {
+                    case 'string': // multi-line string parsing mode
+                        var next = false
+                        while ((next = stream.next()) != null) {
+                            if ((next == '"' || next == "'") && !stream.match(/['"]/, false)) {
+                                state.mode = false
+                                break
+                            }
+                        }
+                        returnType = STRING // continue on in string mode
+                        break
+                    default:
+                        // default parsing mode
+                        var ch = stream.next()
+                        var col = stream.column()
+                        if (col >= 0 && col <= 5) {
+                            returnType = COBOLLINENUM
+                        } else if (col >= 72 && col <= 79) {
+                            stream.skipToEnd()
+                            returnType = MODTAG
+                        } else if (ch == '*' && col == 6) {
+                            // comment
+                            stream.skipToEnd() // rest of the line is a comment
+                            returnType = COMMENT
+                        } else if (ch == '"' || ch == "'") {
+                            state.mode = 'string'
+                            returnType = STRING
+                        } else if (ch == "'" && !tests.digit_or_colon.test(stream.peek())) {
+                            returnType = ATOM
+                        } else if (ch == '.') {
+                            returnType = PERIOD
+                        } else if (isNumber(ch, stream)) {
+                            returnType = NUMBER
+                        } else {
+                            if (stream.current().match(tests.symbol)) {
+                                while (col < 71) {
+                                    if (stream.eat(tests.symbol) === undefined) {
+                                        break
+                                    } else {
+                                        col++
+                                    }
+                                }
+                            }
+                            if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) {
+                                returnType = KEYWORD
+                            } else if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase())) {
+                                returnType = BUILTIN
+                            } else if (atoms && atoms.propertyIsEnumerable(stream.current().toUpperCase())) {
+                                returnType = ATOM
+                            } else returnType = null
+                        }
+                }
+                return returnType
+            },
+            indent: function (state) {
+                if (state.indentStack == null) return state.indentation
+                return state.indentStack.indent
+            },
+        }
+    })
+
+    CodeMirror.defineMIME('text/x-cobol', 'cobol')
+})

+ 210 - 0
libs/codemirror/mode/cobol/index.html

@@ -0,0 +1,210 @@
+<!doctype html>
+
+<title>CodeMirror: COBOL mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<link rel="stylesheet" href="../../theme/neat.css">
+<link rel="stylesheet" href="../../theme/elegant.css">
+<link rel="stylesheet" href="../../theme/erlang-dark.css">
+<link rel="stylesheet" href="../../theme/night.css">
+<link rel="stylesheet" href="../../theme/monokai.css">
+<link rel="stylesheet" href="../../theme/cobalt.css">
+<link rel="stylesheet" href="../../theme/eclipse.css">
+<link rel="stylesheet" href="../../theme/rubyblue.css">
+<link rel="stylesheet" href="../../theme/lesser-dark.css">
+<link rel="stylesheet" href="../../theme/xq-dark.css">
+<link rel="stylesheet" href="../../theme/xq-light.css">
+<link rel="stylesheet" href="../../theme/ambiance.css">
+<link rel="stylesheet" href="../../theme/blackboard.css">
+<link rel="stylesheet" href="../../theme/vibrant-ink.css">
+<link rel="stylesheet" href="../../theme/solarized.css">
+<link rel="stylesheet" href="../../theme/twilight.css">
+<link rel="stylesheet" href="../../theme/midnight.css">
+<link rel="stylesheet" href="../../addon/dialog/dialog.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
+<script src="cobol.js"></script>
+<script src="../../addon/selection/active-line.js"></script>
+<script src="../../addon/search/search.js"></script>
+<script src="../../addon/dialog/dialog.js"></script>
+<script src="../../addon/search/searchcursor.js"></script>
+<style>
+        .CodeMirror {
+          border: 1px solid #eee;
+          font-size : 20px;
+          height : auto !important;
+        }
+        .CodeMirror-activeline-background {background: #555555 !important;}
+    </style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">COBOL</a>
+  </ul>
+</div>
+
+<article>
+<h2>COBOL mode</h2>
+
+    <p> Select Theme <select onchange="selectTheme()" id="selectTheme">
+        <option>default</option>
+        <option>ambiance</option>
+        <option>blackboard</option>
+        <option>cobalt</option>
+        <option>eclipse</option>
+        <option>elegant</option>
+        <option>erlang-dark</option>
+        <option>lesser-dark</option>
+        <option>midnight</option>
+        <option>monokai</option>
+        <option>neat</option>
+        <option>night</option>
+        <option>rubyblue</option>
+        <option>solarized dark</option>
+        <option>solarized light</option>
+        <option selected>twilight</option>
+        <option>vibrant-ink</option>
+        <option>xq-dark</option>
+        <option>xq-light</option>
+    </select>    Select Font Size <select onchange="selectFontsize()" id="selectFontSize">
+          <option value="13px">13px</option>
+          <option value="14px">14px</option>
+          <option value="16px">16px</option>
+          <option value="18px">18px</option>
+          <option value="20px" selected="selected">20px</option>
+          <option value="24px">24px</option>
+          <option value="26px">26px</option>
+          <option value="28px">28px</option>
+          <option value="30px">30px</option>
+          <option value="32px">32px</option>
+          <option value="34px">34px</option>
+          <option value="36px">36px</option>
+        </select>
+<label for="checkBoxReadOnly">Read-only</label>
+<input type="checkbox" id="checkBoxReadOnly" onchange="selectReadOnly()">
+<label for="id_tabToIndentSpace">Insert Spaces on Tab</label>
+<input type="checkbox" id="id_tabToIndentSpace" onchange="tabToIndentSpace()">
+</p>
+<textarea id="code" name="code">
+---------1---------2---------3---------4---------5---------6---------7---------8
+12345678911234567892123456789312345678941234567895123456789612345678971234567898
+000010 IDENTIFICATION DIVISION.                                        MODTGHERE
+000020 PROGRAM-ID.       SAMPLE.
+000030 AUTHOR.           TEST SAM. 
+000040 DATE-WRITTEN.     5 February 2013
+000041
+000042* A sample program just to show the form.
+000043* The program copies its input to the output,
+000044* and counts the number of records.
+000045* At the end this number is printed.
+000046
+000050 ENVIRONMENT DIVISION.
+000060 INPUT-OUTPUT SECTION.
+000070 FILE-CONTROL.
+000080     SELECT STUDENT-FILE     ASSIGN TO SYSIN
+000090         ORGANIZATION IS LINE SEQUENTIAL.
+000100     SELECT PRINT-FILE       ASSIGN TO SYSOUT
+000110         ORGANIZATION IS LINE SEQUENTIAL.
+000120
+000130 DATA DIVISION.
+000140 FILE SECTION.
+000150 FD  STUDENT-FILE
+000160     RECORD CONTAINS 43 CHARACTERS
+000170     DATA RECORD IS STUDENT-IN.
+000180 01  STUDENT-IN              PIC X(43).
+000190
+000200 FD  PRINT-FILE
+000210     RECORD CONTAINS 80 CHARACTERS
+000220     DATA RECORD IS PRINT-LINE.
+000230 01  PRINT-LINE              PIC X(80).
+000240
+000250 WORKING-STORAGE SECTION.
+000260 01  DATA-REMAINS-SWITCH     PIC X(2)      VALUE SPACES.
+000261 01  RECORDS-WRITTEN         PIC 99.
+000270
+000280 01  DETAIL-LINE.
+000290     05  FILLER              PIC X(7)      VALUE SPACES.
+000300     05  RECORD-IMAGE        PIC X(43).
+000310     05  FILLER              PIC X(30)     VALUE SPACES.
+000311 
+000312 01  SUMMARY-LINE.
+000313     05  FILLER              PIC X(7)      VALUE SPACES.
+000314     05  TOTAL-READ          PIC 99.
+000315     05  FILLER              PIC X         VALUE SPACE.
+000316     05  FILLER              PIC X(17)     
+000317                 VALUE  'Records were read'.
+000318     05  FILLER              PIC X(53)     VALUE SPACES.
+000319
+000320 PROCEDURE DIVISION.
+000321
+000330 PREPARE-SENIOR-REPORT.
+000340     OPEN INPUT  STUDENT-FILE
+000350          OUTPUT PRINT-FILE.
+000351     MOVE ZERO TO RECORDS-WRITTEN.
+000360     READ STUDENT-FILE
+000370         AT END MOVE 'NO' TO DATA-REMAINS-SWITCH
+000380     END-READ.
+000390     PERFORM PROCESS-RECORDS
+000410         UNTIL DATA-REMAINS-SWITCH = 'NO'.
+000411     PERFORM PRINT-SUMMARY.
+000420     CLOSE STUDENT-FILE
+000430           PRINT-FILE.
+000440     STOP RUN.
+000450
+000460 PROCESS-RECORDS.
+000470     MOVE STUDENT-IN TO RECORD-IMAGE.
+000480     MOVE DETAIL-LINE TO PRINT-LINE.
+000490     WRITE PRINT-LINE.
+000500     ADD 1 TO RECORDS-WRITTEN.
+000510     READ STUDENT-FILE
+000520         AT END MOVE 'NO' TO DATA-REMAINS-SWITCH
+000530     END-READ. 
+000540
+000550 PRINT-SUMMARY.
+000560     MOVE RECORDS-WRITTEN TO TOTAL-READ.
+000570     MOVE SUMMARY-LINE TO PRINT-LINE.
+000571     WRITE PRINT-LINE. 
+000572
+000580
+</textarea>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        lineNumbers: true,
+        matchBrackets: true,
+        mode: "text/x-cobol",
+        theme : "twilight",
+        styleActiveLine: true,
+        showCursorWhenSelecting : true,  
+      });
+      function selectTheme() {
+        var themeInput = document.getElementById("selectTheme");
+        var theme = themeInput.options[themeInput.selectedIndex].innerHTML;
+        editor.setOption("theme", theme);
+      }
+      function selectFontsize() {
+        var fontSizeInput = document.getElementById("selectFontSize");
+        var fontSize = fontSizeInput.options[fontSizeInput.selectedIndex].innerHTML;
+        editor.getWrapperElement().style.fontSize = fontSize;
+        editor.refresh();
+      }
+      function selectReadOnly() {
+        editor.setOption("readOnly", document.getElementById("checkBoxReadOnly").checked);
+      }
+      function tabToIndentSpace() {
+        if (document.getElementById("id_tabToIndentSpace").checked) {
+            editor.setOption("extraKeys", {Tab: function(cm) { cm.replaceSelection("    ", "end"); }});
+        } else {
+            editor.setOption("extraKeys", {Tab: function(cm) { cm.replaceSelection("    ", "end"); }});
+        }
+      }
+    </script>
+  </article>

+ 348 - 0
libs/codemirror/mode/coffeescript/coffeescript.js

@@ -0,0 +1,348 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/**
+ * Link to the project's GitHub page:
+ * https://github.com/pickhardt/coffeescript-codemirror-mode
+ */
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('coffeescript', function (conf, parserConf) {
+        var ERRORCLASS = 'error'
+
+        function wordRegexp(words) {
+            return new RegExp('^((' + words.join(')|(') + '))\\b')
+        }
+
+        var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/
+        var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/
+        var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/
+        var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/
+
+        var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'isnt', 'in', 'instanceof', 'typeof'])
+        var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else', 'switch', 'try', 'catch', 'finally', 'class']
+        var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete', 'do', 'in', 'of', 'new', 'return', 'then', 'this', '@', 'throw', 'when', 'until', 'extends']
+
+        var keywords = wordRegexp(indentKeywords.concat(commonKeywords))
+
+        indentKeywords = wordRegexp(indentKeywords)
+
+        var stringPrefixes = /^('{3}|\"{3}|['\"])/
+        var regexPrefixes = /^(\/{3}|\/)/
+        var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no']
+        var constants = wordRegexp(commonConstants)
+
+        // Tokenizers
+        function tokenBase(stream, state) {
+            // Handle scope changes
+            if (stream.sol()) {
+                if (state.scope.align === null) state.scope.align = false
+                var scopeOffset = state.scope.offset
+                if (stream.eatSpace()) {
+                    var lineOffset = stream.indentation()
+                    if (lineOffset > scopeOffset && state.scope.type == 'coffee') {
+                        return 'indent'
+                    } else if (lineOffset < scopeOffset) {
+                        return 'dedent'
+                    }
+                    return null
+                } else {
+                    if (scopeOffset > 0) {
+                        dedent(stream, state)
+                    }
+                }
+            }
+            if (stream.eatSpace()) {
+                return null
+            }
+
+            var ch = stream.peek()
+
+            // Handle docco title comment (single line)
+            if (stream.match('####')) {
+                stream.skipToEnd()
+                return 'comment'
+            }
+
+            // Handle multi line comments
+            if (stream.match('###')) {
+                state.tokenize = longComment
+                return state.tokenize(stream, state)
+            }
+
+            // Single line comment
+            if (ch === '#') {
+                stream.skipToEnd()
+                return 'comment'
+            }
+
+            // Handle number literals
+            if (stream.match(/^-?[0-9\.]/, false)) {
+                var floatLiteral = false
+                // Floats
+                if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
+                    floatLiteral = true
+                }
+                if (stream.match(/^-?\d+\.\d*/)) {
+                    floatLiteral = true
+                }
+                if (stream.match(/^-?\.\d+/)) {
+                    floatLiteral = true
+                }
+
+                if (floatLiteral) {
+                    // prevent from getting extra . on 1..
+                    if (stream.peek() == '.') {
+                        stream.backUp(1)
+                    }
+                    return 'number'
+                }
+                // Integers
+                var intLiteral = false
+                // Hex
+                if (stream.match(/^-?0x[0-9a-f]+/i)) {
+                    intLiteral = true
+                }
+                // Decimal
+                if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
+                    intLiteral = true
+                }
+                // Zero by itself with no other piece of number.
+                if (stream.match(/^-?0(?![\dx])/i)) {
+                    intLiteral = true
+                }
+                if (intLiteral) {
+                    return 'number'
+                }
+            }
+
+            // Handle strings
+            if (stream.match(stringPrefixes)) {
+                state.tokenize = tokenFactory(stream.current(), false, 'string')
+                return state.tokenize(stream, state)
+            }
+            // Handle regex literals
+            if (stream.match(regexPrefixes)) {
+                if (stream.current() != '/' || stream.match(/^.*\//, false)) {
+                    // prevent highlight of division
+                    state.tokenize = tokenFactory(stream.current(), true, 'string-2')
+                    return state.tokenize(stream, state)
+                } else {
+                    stream.backUp(1)
+                }
+            }
+
+            // Handle operators and delimiters
+            if (stream.match(operators) || stream.match(wordOperators)) {
+                return 'operator'
+            }
+            if (stream.match(delimiters)) {
+                return 'punctuation'
+            }
+
+            if (stream.match(constants)) {
+                return 'atom'
+            }
+
+            if (stream.match(atProp) || (state.prop && stream.match(identifiers))) {
+                return 'property'
+            }
+
+            if (stream.match(keywords)) {
+                return 'keyword'
+            }
+
+            if (stream.match(identifiers)) {
+                return 'variable'
+            }
+
+            // Handle non-detected items
+            stream.next()
+            return ERRORCLASS
+        }
+
+        function tokenFactory(delimiter, singleline, outclass) {
+            return function (stream, state) {
+                while (!stream.eol()) {
+                    stream.eatWhile(/[^'"\/\\]/)
+                    if (stream.eat('\\')) {
+                        stream.next()
+                        if (singleline && stream.eol()) {
+                            return outclass
+                        }
+                    } else if (stream.match(delimiter)) {
+                        state.tokenize = tokenBase
+                        return outclass
+                    } else {
+                        stream.eat(/['"\/]/)
+                    }
+                }
+                if (singleline) {
+                    if (parserConf.singleLineStringErrors) {
+                        outclass = ERRORCLASS
+                    } else {
+                        state.tokenize = tokenBase
+                    }
+                }
+                return outclass
+            }
+        }
+
+        function longComment(stream, state) {
+            while (!stream.eol()) {
+                stream.eatWhile(/[^#]/)
+                if (stream.match('###')) {
+                    state.tokenize = tokenBase
+                    break
+                }
+                stream.eatWhile('#')
+            }
+            return 'comment'
+        }
+
+        function indent(stream, state, type) {
+            type = type || 'coffee'
+            var offset = 0,
+                align = false,
+                alignOffset = null
+            for (var scope = state.scope; scope; scope = scope.prev) {
+                if (scope.type === 'coffee' || scope.type == '}') {
+                    offset = scope.offset + conf.indentUnit
+                    break
+                }
+            }
+            if (type !== 'coffee') {
+                align = null
+                alignOffset = stream.column() + stream.current().length
+            } else if (state.scope.align) {
+                state.scope.align = false
+            }
+            state.scope = {
+                offset: offset,
+                type: type,
+                prev: state.scope,
+                align: align,
+                alignOffset: alignOffset,
+            }
+        }
+
+        function dedent(stream, state) {
+            if (!state.scope.prev) return
+            if (state.scope.type === 'coffee') {
+                var _indent = stream.indentation()
+                var matched = false
+                for (var scope = state.scope; scope; scope = scope.prev) {
+                    if (_indent === scope.offset) {
+                        matched = true
+                        break
+                    }
+                }
+                if (!matched) {
+                    return true
+                }
+                while (state.scope.prev && state.scope.offset !== _indent) {
+                    state.scope = state.scope.prev
+                }
+                return false
+            } else {
+                state.scope = state.scope.prev
+                return false
+            }
+        }
+
+        function tokenLexer(stream, state) {
+            var style = state.tokenize(stream, state)
+            var current = stream.current()
+
+            // Handle scope changes.
+            if (current === 'return') {
+                state.dedent = true
+            }
+            if (((current === '->' || current === '=>') && stream.eol()) || style === 'indent') {
+                indent(stream, state)
+            }
+            var delimiter_index = '[({'.indexOf(current)
+            if (delimiter_index !== -1) {
+                indent(stream, state, '])}'.slice(delimiter_index, delimiter_index + 1))
+            }
+            if (indentKeywords.exec(current)) {
+                indent(stream, state)
+            }
+            if (current == 'then') {
+                dedent(stream, state)
+            }
+
+            if (style === 'dedent') {
+                if (dedent(stream, state)) {
+                    return ERRORCLASS
+                }
+            }
+            delimiter_index = '])}'.indexOf(current)
+            if (delimiter_index !== -1) {
+                while (state.scope.type == 'coffee' && state.scope.prev) state.scope = state.scope.prev
+                if (state.scope.type == current) state.scope = state.scope.prev
+            }
+            if (state.dedent && stream.eol()) {
+                if (state.scope.type == 'coffee' && state.scope.prev) state.scope = state.scope.prev
+                state.dedent = false
+            }
+
+            return style
+        }
+
+        var external = {
+            startState: function (basecolumn) {
+                return {
+                    tokenize: tokenBase,
+                    scope: { offset: basecolumn || 0, type: 'coffee', prev: null, align: false },
+                    prop: false,
+                    dedent: 0,
+                }
+            },
+
+            token: function (stream, state) {
+                var fillAlign = state.scope.align === null && state.scope
+                if (fillAlign && stream.sol()) fillAlign.align = false
+
+                var style = tokenLexer(stream, state)
+                if (style && style != 'comment') {
+                    if (fillAlign) fillAlign.align = true
+                    state.prop = style == 'punctuation' && stream.current() == '.'
+                }
+
+                return style
+            },
+
+            indent: function (state, text) {
+                if (state.tokenize != tokenBase) return 0
+                var scope = state.scope
+                var closer = text && '])}'.indexOf(text.charAt(0)) > -1
+                if (closer) while (scope.type == 'coffee' && scope.prev) scope = scope.prev
+                var closes = closer && scope.type === text.charAt(0)
+                if (scope.align) return scope.alignOffset - (closes ? 1 : 0)
+                else return (closes ? scope.prev : scope).offset
+            },
+
+            lineComment: '#',
+            fold: 'indent',
+        }
+        return external
+    })
+
+    // IANA registered media type
+    // https://www.iana.org/assignments/media-types/
+    CodeMirror.defineMIME('application/vnd.coffeescript', 'coffeescript')
+
+    CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript')
+    CodeMirror.defineMIME('text/coffeescript', 'coffeescript')
+})

+ 740 - 0
libs/codemirror/mode/coffeescript/index.html

@@ -0,0 +1,740 @@
+<!doctype html>
+
+<title>CodeMirror: CoffeeScript mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="coffeescript.js"></script>
+<style>.CodeMirror {border-top: 1px solid silver; border-bottom: 1px solid silver;}</style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">CoffeeScript</a>
+  </ul>
+</div>
+
+<article>
+<h2>CoffeeScript mode</h2>
+<form><textarea id="code" name="code">
+# CoffeeScript mode for CodeMirror
+# Copyright (c) 2011 Jeff Pickhardt, released under
+# the MIT License.
+#
+# Modified from the Python CodeMirror mode, which also is 
+# under the MIT License Copyright (c) 2010 Timothy Farrell.
+#
+# The following script, Underscore.coffee, is used to 
+# demonstrate CoffeeScript mode for CodeMirror.
+#
+# To download CoffeeScript mode for CodeMirror, go to:
+# https://github.com/pickhardt/coffeescript-codemirror-mode
+
+# **Underscore.coffee
+# (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.**
+# Underscore is freely distributable under the terms of the
+# [MIT license](http://en.wikipedia.org/wiki/MIT_License).
+# Portions of Underscore are inspired by or borrowed from
+# [Prototype.js](http://prototypejs.org/api), Oliver Steele's
+# [Functional](http://osteele.com), and John Resig's
+# [Micro-Templating](http://ejohn.org).
+# For all details and documentation:
+# http://documentcloud.github.com/underscore/
+
+
+# Baseline setup
+# --------------
+
+# Establish the root object, `window` in the browser, or `global` on the server.
+root = this
+
+
+# Save the previous value of the `_` variable.
+previousUnderscore = root._
+
+### Multiline
+    comment
+###
+
+# Establish the object that gets thrown to break out of a loop iteration.
+# `StopIteration` is SOP on Mozilla.
+breaker = if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
+
+
+#### Docco style single line comment (title)
+
+
+# Helper function to escape **RegExp** contents, because JS doesn't have one.
+escapeRegExp = (string) -> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1')
+
+
+# Save bytes in the minified (but not gzipped) version:
+ArrayProto = Array.prototype
+ObjProto = Object.prototype
+
+
+# Create quick reference variables for speed access to core prototypes.
+slice = ArrayProto.slice
+unshift = ArrayProto.unshift
+toString = ObjProto.toString
+hasOwnProperty = ObjProto.hasOwnProperty
+propertyIsEnumerable = ObjProto.propertyIsEnumerable
+
+
+# All **ECMA5** native implementations we hope to use are declared here.
+nativeForEach = ArrayProto.forEach
+nativeMap = ArrayProto.map
+nativeReduce = ArrayProto.reduce
+nativeReduceRight = ArrayProto.reduceRight
+nativeFilter = ArrayProto.filter
+nativeEvery = ArrayProto.every
+nativeSome = ArrayProto.some
+nativeIndexOf = ArrayProto.indexOf
+nativeLastIndexOf = ArrayProto.lastIndexOf
+nativeIsArray = Array.isArray
+nativeKeys = Object.keys
+
+
+# Create a safe reference to the Underscore object for use below.
+_ = (obj) -> new wrapper(obj)
+
+
+# Export the Underscore object for **CommonJS**.
+if typeof(exports) != 'undefined' then exports._ = _
+
+
+# Export Underscore to global scope.
+root._ = _
+
+
+# Current version.
+_.VERSION = '1.1.0'
+
+
+# Collection Functions
+# --------------------
+
+# The cornerstone, an **each** implementation.
+# Handles objects implementing **forEach**, arrays, and raw objects.
+_.each = (obj, iterator, context) ->
+  try
+    if nativeForEach and obj.forEach is nativeForEach
+      obj.forEach iterator, context
+    else if _.isNumber obj.length
+      iterator.call context, obj[i], i, obj for i in [0...obj.length]
+    else
+      iterator.call context, val, key, obj for own key, val of obj
+  catch e
+    throw e if e isnt breaker
+  obj
+
+
+# Return the results of applying the iterator to each element. Use JavaScript
+# 1.6's version of **map**, if possible.
+_.map = (obj, iterator, context) ->
+  return obj.map(iterator, context) if nativeMap and obj.map is nativeMap
+  results = []
+  _.each obj, (value, index, list) ->
+    results.push iterator.call context, value, index, list
+  results
+
+
+# **Reduce** builds up a single result from a list of values. Also known as
+# **inject**, or **foldl**. Uses JavaScript 1.8's version of **reduce**, if possible.
+_.reduce = (obj, iterator, memo, context) ->
+  if nativeReduce and obj.reduce is nativeReduce
+    iterator = _.bind iterator, context if context
+    return obj.reduce iterator, memo
+  _.each obj, (value, index, list) ->
+    memo = iterator.call context, memo, value, index, list
+  memo
+
+
+# The right-associative version of **reduce**, also known as **foldr**. Uses
+# JavaScript 1.8's version of **reduceRight**, if available.
+_.reduceRight = (obj, iterator, memo, context) ->
+  if nativeReduceRight and obj.reduceRight is nativeReduceRight
+    iterator = _.bind iterator, context if context
+    return obj.reduceRight iterator, memo
+  reversed = _.clone(_.toArray(obj)).reverse()
+  _.reduce reversed, iterator, memo, context
+
+
+# Return the first value which passes a truth test.
+_.detect = (obj, iterator, context) ->
+  result = null
+  _.each obj, (value, index, list) ->
+    if iterator.call context, value, index, list
+      result = value
+      _.breakLoop()
+  result
+
+
+# Return all the elements that pass a truth test. Use JavaScript 1.6's
+# **filter**, if it exists.
+_.filter = (obj, iterator, context) ->
+  return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter
+  results = []
+  _.each obj, (value, index, list) ->
+    results.push value if iterator.call context, value, index, list
+  results
+
+
+# Return all the elements for which a truth test fails.
+_.reject = (obj, iterator, context) ->
+  results = []
+  _.each obj, (value, index, list) ->
+    results.push value if not iterator.call context, value, index, list
+  results
+
+
+# Determine whether all of the elements match a truth test. Delegate to
+# JavaScript 1.6's **every**, if it is present.
+_.every = (obj, iterator, context) ->
+  iterator ||= _.identity
+  return obj.every iterator, context if nativeEvery and obj.every is nativeEvery
+  result = true
+  _.each obj, (value, index, list) ->
+    _.breakLoop() unless (result = result and iterator.call(context, value, index, list))
+  result
+
+
+# Determine if at least one element in the object matches a truth test. Use
+# JavaScript 1.6's **some**, if it exists.
+_.some = (obj, iterator, context) ->
+  iterator ||= _.identity
+  return obj.some iterator, context if nativeSome and obj.some is nativeSome
+  result = false
+  _.each obj, (value, index, list) ->
+    _.breakLoop() if (result = iterator.call(context, value, index, list))
+  result
+
+
+# Determine if a given value is included in the array or object,
+# based on `===`.
+_.include = (obj, target) ->
+  return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf
+  return true for own key, val of obj when val is target
+  false
+
+
+# Invoke a method with arguments on every item in a collection.
+_.invoke = (obj, method) ->
+  args = _.rest arguments, 2
+  (if method then val[method] else val).apply(val, args) for val in obj
+
+
+# Convenience version of a common use case of **map**: fetching a property.
+_.pluck = (obj, key) ->
+  _.map(obj, (val) -> val[key])
+
+
+# Return the maximum item or (item-based computation).
+_.max = (obj, iterator, context) ->
+  return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
+  result = computed: -Infinity
+  _.each obj, (value, index, list) ->
+    computed = if iterator then iterator.call(context, value, index, list) else value
+    computed >= result.computed and (result = {value: value, computed: computed})
+  result.value
+
+
+# Return the minimum element (or element-based computation).
+_.min = (obj, iterator, context) ->
+  return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
+  result = computed: Infinity
+  _.each obj, (value, index, list) ->
+    computed = if iterator then iterator.call(context, value, index, list) else value
+    computed < result.computed and (result = {value: value, computed: computed})
+  result.value
+
+
+# Sort the object's values by a criterion produced by an iterator.
+_.sortBy = (obj, iterator, context) ->
+  _.pluck(((_.map obj, (value, index, list) ->
+    {value: value, criteria: iterator.call(context, value, index, list)}
+  ).sort((left, right) ->
+    a = left.criteria; b = right.criteria
+    if a < b then -1 else if a > b then 1 else 0
+  )), 'value')
+
+
+# Use a comparator function to figure out at what index an object should
+# be inserted so as to maintain order. Uses binary search.
+_.sortedIndex = (array, obj, iterator) ->
+  iterator ||= _.identity
+  low = 0
+  high = array.length
+  while low < high
+    mid = (low + high) >> 1
+    if iterator(array[mid]) < iterator(obj) then low = mid + 1 else high = mid
+  low
+
+
+# Convert anything iterable into a real, live array.
+_.toArray = (iterable) ->
+  return [] if (!iterable)
+  return iterable.toArray() if (iterable.toArray)
+  return iterable if (_.isArray(iterable))
+  return slice.call(iterable) if (_.isArguments(iterable))
+  _.values(iterable)
+
+
+# Return the number of elements in an object.
+_.size = (obj) -> _.toArray(obj).length
+
+
+# Array Functions
+# ---------------
+
+# Get the first element of an array. Passing `n` will return the first N
+# values in the array. Aliased as **head**. The `guard` check allows it to work
+# with **map**.
+_.first = (array, n, guard) ->
+  if n and not guard then slice.call(array, 0, n) else array[0]
+
+
+# Returns everything but the first entry of the array. Aliased as **tail**.
+# Especially useful on the arguments object. Passing an `index` will return
+# the rest of the values in the array from that index onward. The `guard`
+# check allows it to work with **map**.
+_.rest = (array, index, guard) ->
+  slice.call(array, if _.isUndefined(index) or guard then 1 else index)
+
+
+# Get the last element of an array.
+_.last = (array) -> array[array.length - 1]
+
+
+# Trim out all falsy values from an array.
+_.compact = (array) -> item for item in array when item
+
+
+# Return a completely flattened version of an array.
+_.flatten = (array) ->
+  _.reduce array, (memo, value) ->
+    return memo.concat(_.flatten(value)) if _.isArray value
+    memo.push value
+    memo
+  , []
+
+
+# Return a version of the array that does not contain the specified value(s).
+_.without = (array) ->
+  values = _.rest arguments
+  val for val in _.toArray(array) when not _.include values, val
+
+
+# Produce a duplicate-free version of the array. If the array has already
+# been sorted, you have the option of using a faster algorithm.
+_.uniq = (array, isSorted) ->
+  memo = []
+  for el, i in _.toArray array
+    memo.push el if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
+  memo
+
+
+# Produce an array that contains every item shared between all the
+# passed-in arrays.
+_.intersect = (array) ->
+  rest = _.rest arguments
+  _.select _.uniq(array), (item) ->
+    _.all rest, (other) ->
+      _.indexOf(other, item) >= 0
+
+
+# Zip together multiple lists into a single array -- elements that share
+# an index go together.
+_.zip = ->
+  length = _.max _.pluck arguments, 'length'
+  results = new Array length
+  for i in [0...length]
+    results[i] = _.pluck arguments, String i
+  results
+
+
+# If the browser doesn't supply us with **indexOf** (I'm looking at you, MSIE),
+# we need this function. Return the position of the first occurrence of an
+# item in an array, or -1 if the item is not included in the array.
+_.indexOf = (array, item) ->
+  return array.indexOf item if nativeIndexOf and array.indexOf is nativeIndexOf
+  i = 0; l = array.length
+  while l - i
+    if array[i] is item then return i else i++
+  -1
+
+
+# Provide JavaScript 1.6's **lastIndexOf**, delegating to the native function,
+# if possible.
+_.lastIndexOf = (array, item) ->
+  return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf
+  i = array.length
+  while i
+    if array[i] is item then return i else i--
+  -1
+
+
+# Generate an integer Array containing an arithmetic progression. A port of
+# [the native Python **range** function](http://docs.python.org/library/functions.html#range).
+_.range = (start, stop, step) ->
+  a = arguments
+  solo = a.length <= 1
+  i = start = if solo then 0 else a[0]
+  stop = if solo then a[0] else a[1]
+  step = a[2] or 1
+  len = Math.ceil((stop - start) / step)
+  return [] if len <= 0
+  range = new Array len
+  idx = 0
+  loop
+    return range if (if step > 0 then i - stop else stop - i) >= 0
+    range[idx] = i
+    idx++
+    i+= step
+
+
+# Function Functions
+# ------------------
+
+# Create a function bound to a given object (assigning `this`, and arguments,
+# optionally). Binding with arguments is also known as **curry**.
+_.bind = (func, obj) ->
+  args = _.rest arguments, 2
+  -> func.apply obj or root, args.concat arguments
+
+
+# Bind all of an object's methods to that object. Useful for ensuring that
+# all callbacks defined on an object belong to it.
+_.bindAll = (obj) ->
+  funcs = if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
+  _.each funcs, (f) -> obj[f] = _.bind obj[f], obj
+  obj
+
+
+# Delays a function for the given number of milliseconds, and then calls
+# it with the arguments supplied.
+_.delay = (func, wait) ->
+  args = _.rest arguments, 2
+  setTimeout((-> func.apply(func, args)), wait)
+
+
+# Memoize an expensive function by storing its results.
+_.memoize = (func, hasher) ->
+  memo = {}
+  hasher or= _.identity
+  ->
+    key = hasher.apply this, arguments
+    return memo[key] if key of memo
+    memo[key] = func.apply this, arguments
+
+
+# Defers a function, scheduling it to run after the current call stack has
+# cleared.
+_.defer = (func) ->
+  _.delay.apply _, [func, 1].concat _.rest arguments
+
+
+# Returns the first function passed as an argument to the second,
+# allowing you to adjust arguments, run code before and after, and
+# conditionally execute the original function.
+_.wrap = (func, wrapper) ->
+  -> wrapper.apply wrapper, [func].concat arguments
+
+
+# Returns a function that is the composition of a list of functions, each
+# consuming the return value of the function that follows.
+_.compose = ->
+  funcs = arguments
+  ->
+    args = arguments
+    for i in [funcs.length - 1..0] by -1
+      args = [funcs[i].apply(this, args)]
+    args[0]
+
+
+# Object Functions
+# ----------------
+
+# Retrieve the names of an object's properties.
+_.keys = nativeKeys or (obj) ->
+  return _.range 0, obj.length if _.isArray(obj)
+  key for key, val of obj
+
+
+# Retrieve the values of an object's properties.
+_.values = (obj) ->
+  _.map obj, _.identity
+
+
+# Return a sorted list of the function names available in Underscore.
+_.functions = (obj) ->
+  _.filter(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
+
+
+# Extend a given object with all of the properties in a source object.
+_.extend = (obj) ->
+  for source in _.rest(arguments)
+    obj[key] = val for key, val of source
+  obj
+
+
+# Create a (shallow-cloned) duplicate of an object.
+_.clone = (obj) ->
+  return obj.slice 0 if _.isArray obj
+  _.extend {}, obj
+
+
+# Invokes interceptor with the obj, and then returns obj.
+# The primary purpose of this method is to "tap into" a method chain,
+# in order to perform operations on intermediate results within
+ the chain.
+_.tap = (obj, interceptor) ->
+  interceptor obj
+  obj
+
+
+# Perform a deep comparison to check if two objects are equal.
+_.isEqual = (a, b) ->
+  # Check object identity.
+  return true if a is b
+  # Different types?
+  atype = typeof(a); btype = typeof(b)
+  return false if atype isnt btype
+  # Basic equality test (watch out for coercions).
+  return true if `a == b`
+  # One is falsy and the other truthy.
+  return false if (!a and b) or (a and !b)
+  # One of them implements an `isEqual()`?
+  return a.isEqual(b) if a.isEqual
+  # Check dates' integer values.
+  return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b)
+  # Both are NaN?
+  return false if _.isNaN(a) and _.isNaN(b)
+  # Compare regular expressions.
+  if _.isRegExp(a) and _.isRegExp(b)
+    return a.source is b.source and
+           a.global is b.global and
+           a.ignoreCase is b.ignoreCase and
+           a.multiline is b.multiline
+  # If a is not an object by this point, we can't handle it.
+  return false if atype isnt 'object'
+  # Check for different array lengths before comparing contents.
+  return false if a.length and (a.length isnt b.length)
+  # Nothing else worked, deep compare the contents.
+  aKeys = _.keys(a); bKeys = _.keys(b)
+  # Different object sizes?
+  return false if aKeys.length isnt bKeys.length
+  # Recursive comparison of contents.
+  return false for key, val of a when !(key of b) or !_.isEqual(val, b[key])
+  true
+
+
+# Is a given array or object empty?
+_.isEmpty = (obj) ->
+  return obj.length is 0 if _.isArray(obj) or _.isString(obj)
+  return false for own key of obj
+  true
+
+
+# Is a given value a DOM element?
+_.isElement = (obj) -> obj and obj.nodeType is 1
+
+
+# Is a given value an array?
+_.isArray = nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift and not obj.callee)
+
+
+# Is a given variable an arguments object?
+_.isArguments = (obj) -> obj and obj.callee
+
+
+# Is the given value a function?
+_.isFunction = (obj) -> !!(obj and obj.constructor and obj.call and obj.apply)
+
+
+# Is the given value a string?
+_.isString = (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
+
+
+# Is a given value a number?
+_.isNumber = (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
+
+
+# Is a given value a boolean?
+_.isBoolean = (obj) -> obj is true or obj is false
+
+
+# Is a given value a Date?
+_.isDate = (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
+
+
+# Is the given value a regular expression?
+_.isRegExp = (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
+
+
+# Is the given value NaN -- this one is interesting. `NaN != NaN`, and
+# `isNaN(undefined) == true`, so we make sure it's a number first.
+_.isNaN = (obj) -> _.isNumber(obj) and window.isNaN(obj)
+
+
+# Is a given value equal to null?
+_.isNull = (obj) -> obj is null
+
+
+# Is a given variable undefined?
+_.isUndefined = (obj) -> typeof obj is 'undefined'
+
+
+# Utility Functions
+# -----------------
+
+# Run Underscore.js in noConflict mode, returning the `_` variable to its
+# previous owner. Returns a reference to the Underscore object.
+_.noConflict = ->
+  root._ = previousUnderscore
+  this
+
+
+# Keep the identity function around for default iterators.
+_.identity = (value) -> value
+
+
+# Run a function `n` times.
+_.times = (n, iterator, context) ->
+  iterator.call context, i for i in [0...n]
+
+
+# Break out of the middle of an iteration.
+_.breakLoop = -> throw breaker
+
+
+# Add your own custom functions to the Underscore object, ensuring that
+# they're correctly added to the OOP wrapper as well.
+_.mixin = (obj) ->
+  for name in _.functions(obj)
+    addToWrapper name, _[name] = obj[name]
+
+
+# Generate a unique integer id (unique within the entire client session).
+# Useful for temporary DOM ids.
+idCounter = 0
+_.uniqueId = (prefix) ->
+  (prefix or '') + idCounter++
+
+
+# By default, Underscore uses **ERB**-style template delimiters, change the
+# following template settings to use alternative delimiters.
+_.templateSettings = {
+  start: '<%'
+  end: '%>'
+  interpolate: /<%=(.+?)%>/g
+}
+
+
+# JavaScript templating a-la **ERB**, pilfered from John Resig's
+# *Secrets of the JavaScript Ninja*, page 83.
+# Single-quote fix from Rick Strahl.
+# With alterations for arbitrary delimiters, and to preserve whitespace.
+_.template = (str, data) ->
+  c = _.templateSettings
+  endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g")
+  fn = new Function 'obj',
+    'var p=[],print=function(){p.push.apply(p,arguments);};' +
+    'with(obj||{}){p.push(\'' +
+    str.replace(/\r/g, '\\r')
+       .replace(/\n/g, '\\n')
+       .replace(/\t/g, '\\t')
+       .replace(endMatch,"���")
+       .split("'").join("\\'")
+       .split("���").join("'")
+       .replace(c.interpolate, "',$1,'")
+       .split(c.start).join("');")
+       .split(c.end).join("p.push('") +
+       "');}return p.join('');"
+  if data then fn(data) else fn
+
+
+# Aliases
+# -------
+
+_.forEach = _.each
+_.foldl = _.inject = _.reduce
+_.foldr = _.reduceRight
+_.select = _.filter
+_.all = _.every
+_.any = _.some
+_.contains = _.include
+_.head = _.first
+_.tail = _.rest
+_.methods = _.functions
+
+
+# Setup the OOP Wrapper
+# ---------------------
+
+# If Underscore is called as a function, it returns a wrapped object that
+# can be used OO-style. This wrapper holds altered versions of all the
+# underscore functions. Wrapped objects may be chained.
+wrapper = (obj) ->
+  this._wrapped = obj
+  this
+
+
+# Helper function to continue chaining intermediate results.
+result = (obj, chain) ->
+  if chain then _(obj).chain() else obj
+
+
+# A method to easily add functions to the OOP wrapper.
+addToWrapper = (name, func) ->
+  wrapper.prototype[name] = ->
+    args = _.toArray arguments
+    unshift.call args, this._wrapped
+    result func.apply(_, args), this._chain
+
+
+# Add all ofthe Underscore functions to the wrapper object.
+_.mixin _
+
+
+# Add all mutator Array functions to the wrapper.
+_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->
+  method = Array.prototype[name]
+  wrapper.prototype[name] = ->
+    method.apply(this._wrapped, arguments)
+    result(this._wrapped, this._chain)
+
+
+# Add all accessor Array functions to the wrapper.
+_.each ['concat', 'join', 'slice'], (name) ->
+  method = Array.prototype[name]
+  wrapper.prototype[name] = ->
+    result(method.apply(this._wrapped, arguments), this._chain)
+
+
+# Start chaining a wrapped Underscore object.
+wrapper::chain = ->
+  this._chain = true
+  this
+
+
+# Extracts the result from a wrapped and chained object.
+wrapper::value = -> this._wrapped
+</textarea></form>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
+    </script>
+
+    <p><strong>MIME types defined:</strong> <code>application/vnd.coffeescript</code>, <code>text/coffeescript</code>, <code>text/x-coffeescript</code>.</p>
+
+    <p>The CoffeeScript mode was written by Jeff Pickhardt.</p>
+
+  </article>

+ 156 - 0
libs/codemirror/mode/commonlisp/commonlisp.js

@@ -0,0 +1,156 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('commonlisp', function (config) {
+        var specialForm =
+            /^(block|let*|return-from|catch|load-time-value|setq|eval-when|locally|symbol-macrolet|flet|macrolet|tagbody|function|multiple-value-call|the|go|multiple-value-prog1|throw|if|progn|unwind-protect|labels|progv|let|quote)$/
+        var assumeBody = /^with|^def|^do|^prog|case$|^cond$|bind$|when$|unless$/
+        var numLiteral = /^(?:[+\-]?(?:\d+|\d*\.\d+)(?:[efd][+\-]?\d+)?|[+\-]?\d+(?:\/[+\-]?\d+)?|#b[+\-]?[01]+|#o[+\-]?[0-7]+|#x[+\-]?[\da-f]+)/
+        var symbol = /[^\s'`,@()\[\]";]/
+        var type
+
+        function readSym(stream) {
+            var ch
+            while ((ch = stream.next())) {
+                if (ch == '\\') stream.next()
+                else if (!symbol.test(ch)) {
+                    stream.backUp(1)
+                    break
+                }
+            }
+            return stream.current()
+        }
+
+        function base(stream, state) {
+            if (stream.eatSpace()) {
+                type = 'ws'
+                return null
+            }
+            if (stream.match(numLiteral)) return 'number'
+            var ch = stream.next()
+            if (ch == '\\') ch = stream.next()
+
+            if (ch == '"') return (state.tokenize = inString)(stream, state)
+            else if (ch == '(') {
+                type = 'open'
+                return 'bracket'
+            } else if (ch == ')' || ch == ']') {
+                type = 'close'
+                return 'bracket'
+            } else if (ch == ';') {
+                stream.skipToEnd()
+                type = 'ws'
+                return 'comment'
+            } else if (/['`,@]/.test(ch)) return null
+            else if (ch == '|') {
+                if (stream.skipTo('|')) {
+                    stream.next()
+                    return 'symbol'
+                } else {
+                    stream.skipToEnd()
+                    return 'error'
+                }
+            } else if (ch == '#') {
+                var ch = stream.next()
+                if (ch == '(') {
+                    type = 'open'
+                    return 'bracket'
+                } else if (/[+\-=\.']/.test(ch)) return null
+                else if (/\d/.test(ch) && stream.match(/^\d*#/)) return null
+                else if (ch == '|') return (state.tokenize = inComment)(stream, state)
+                else if (ch == ':') {
+                    readSym(stream)
+                    return 'meta'
+                } else if (ch == '\\') {
+                    stream.next()
+                    readSym(stream)
+                    return 'string-2'
+                } else return 'error'
+            } else {
+                var name = readSym(stream)
+                if (name == '.') return null
+                type = 'symbol'
+                if (name == 'nil' || name == 't' || name.charAt(0) == ':') return 'atom'
+                if (state.lastType == 'open' && (specialForm.test(name) || assumeBody.test(name))) return 'keyword'
+                if (name.charAt(0) == '&') return 'variable-2'
+                return 'variable'
+            }
+        }
+
+        function inString(stream, state) {
+            var escaped = false,
+                next
+            while ((next = stream.next())) {
+                if (next == '"' && !escaped) {
+                    state.tokenize = base
+                    break
+                }
+                escaped = !escaped && next == '\\'
+            }
+            return 'string'
+        }
+
+        function inComment(stream, state) {
+            var next, last
+            while ((next = stream.next())) {
+                if (next == '#' && last == '|') {
+                    state.tokenize = base
+                    break
+                }
+                last = next
+            }
+            type = 'ws'
+            return 'comment'
+        }
+
+        return {
+            startState: function () {
+                return { ctx: { prev: null, start: 0, indentTo: 0 }, lastType: null, tokenize: base }
+            },
+
+            token: function (stream, state) {
+                if (stream.sol() && typeof state.ctx.indentTo != 'number') state.ctx.indentTo = state.ctx.start + 1
+
+                type = null
+                var style = state.tokenize(stream, state)
+                if (type != 'ws') {
+                    if (state.ctx.indentTo == null) {
+                        if (type == 'symbol' && assumeBody.test(stream.current())) state.ctx.indentTo = state.ctx.start + config.indentUnit
+                        else state.ctx.indentTo = 'next'
+                    } else if (state.ctx.indentTo == 'next') {
+                        state.ctx.indentTo = stream.column()
+                    }
+                    state.lastType = type
+                }
+                if (type == 'open') state.ctx = { prev: state.ctx, start: stream.column(), indentTo: null }
+                else if (type == 'close') state.ctx = state.ctx.prev || state.ctx
+                return style
+            },
+
+            indent: function (state, _textAfter) {
+                var i = state.ctx.indentTo
+                return typeof i == 'number' ? i : state.ctx.start + 1
+            },
+
+            closeBrackets: { pairs: '()[]{}""' },
+            lineComment: ';;',
+            fold: 'brace-paren',
+            blockCommentStart: '#|',
+            blockCommentEnd: '|#',
+        }
+    })
+
+    CodeMirror.defineMIME('text/x-common-lisp', 'commonlisp')
+})

+ 177 - 0
libs/codemirror/mode/commonlisp/index.html

@@ -0,0 +1,177 @@
+<!doctype html>
+
+<title>CodeMirror: Common Lisp mode</title>
+<meta charset="utf-8"/>
+<link rel=stylesheet href="../../doc/docs.css">
+
+<link rel="stylesheet" href="../../lib/codemirror.css">
+<script src="../../lib/codemirror.js"></script>
+<script src="commonlisp.js"></script>
+<style>.CodeMirror {background: #f8f8f8;}</style>
+<div id=nav>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png" alt=""></a>
+
+  <ul>
+    <li><a href="../../index.html">Home</a>
+    <li><a href="../../doc/manual.html">Manual</a>
+    <li><a href="https://github.com/codemirror/codemirror">Code</a>
+  </ul>
+  <ul>
+    <li><a href="../index.html">Language modes</a>
+    <li><a class=active href="#">Common Lisp</a>
+  </ul>
+</div>
+
+<article>
+<h2>Common Lisp mode</h2>
+<form><textarea id="code" name="code">(in-package :cl-postgres)
+
+;; These are used to synthesize reader and writer names for integer
+;; reading/writing functions when the amount of bytes and the
+;; signedness is known. Both the macro that creates the functions and
+;; some macros that use them create names this way.
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defun integer-reader-name (bytes signed)
+    (intern (with-standard-io-syntax
+              (format nil "~a~a~a~a" '#:read- (if signed "" '#:u) '#:int bytes))))
+  (defun integer-writer-name (bytes signed)
+    (intern (with-standard-io-syntax
+              (format nil "~a~a~a~a" '#:write- (if signed "" '#:u) '#:int bytes)))))
+
+(defmacro integer-reader (bytes)
+  "Create a function to read integers from a binary stream."
+  (let ((bits (* bytes 8)))
+    (labels ((return-form (signed)
+               (if signed
+                   `(if (logbitp ,(1- bits) result)
+                        (dpb result (byte ,(1- bits) 0) -1)
+                        result)
+                   `result))
+             (generate-reader (signed)
+               `(defun ,(integer-reader-name bytes signed) (socket)
+                  (declare (type stream socket)
+                           #.*optimize*)
+                  ,(if (= bytes 1)
+                       `(let ((result (the (unsigned-byte 8) (read-byte socket))))
+                          (declare (type (unsigned-byte 8) result))
+                          ,(return-form signed))
+                       `(let ((result 0))
+                          (declare (type (unsigned-byte ,bits) result))
+                          ,@(loop :for byte :from (1- bytes) :downto 0
+                                   :collect `(setf (ldb (byte 8 ,(* 8 byte)) result)
+                                                   (the (unsigned-byte 8) (read-byte socket))))
+                          ,(return-form signed))))))
+      `(progn
+;; This causes weird errors on SBCL in some circumstances. Disabled for now.
+;;         (declaim (inline ,(integer-reader-name bytes t)
+;;                          ,(integer-reader-name bytes nil)))
+         (declaim (ftype (function (t) (signed-byte ,bits))
+                         ,(integer-reader-name bytes t)))
+         ,(generate-reader t)
+         (declaim (ftype (function (t) (unsigned-byte ,bits))
+                         ,(integer-reader-name bytes nil)))
+         ,(generate-reader nil)))))
+
+(defmacro integer-writer (bytes)
+  "Create a function to write integers to a binary stream."
+  (let ((bits (* 8 bytes)))
+    `(progn
+      (declaim (inline ,(integer-writer-name bytes t)
+                       ,(integer-writer-name bytes nil)))
+      (defun ,(integer-writer-name bytes nil) (socket value)
+        (declare (type stream socket)
+                 (type (unsigned-byte ,bits) value)
+                 #.*optimize*)
+        ,@(if (= bytes 1)
+              `((write-byte value socket))
+              (loop :for byte :from (1- bytes) :downto 0
+                    :collect `(write-byte (ldb (byte 8 ,(* byte 8)) value)
+                               socket)))
+        (values))
+      (defun ,(integer-writer-name bytes t) (socket value)
+        (declare (type stream socket)
+                 (type (signed-byte ,bits) value)
+                 #.*optimize*)
+        ,@(if (= bytes 1)
+              `((write-byte (ldb (byte 8 0) value) socket))
+              (loop :for byte :from (1- bytes) :downto 0
+                    :collect `(write-byte (ldb (byte 8 ,(* byte 8)) value)
+                               socket)))
+        (values)))))
+
+;; All the instances of the above that we need.
+
+(integer-reader 1)
+(integer-reader 2)
+(integer-reader 4)
+(integer-reader 8)
+
+(integer-writer 1)
+(integer-writer 2)
+(integer-writer 4)
+
+(defun write-bytes (socket bytes)
+  "Write a byte-array to a stream."
+  (declare (type stream socket)
+           (type (simple-array (unsigned-byte 8)) bytes)
+           #.*optimize*)
+  (write-sequence bytes socket))
+
+(defun write-str (socket string)
+  "Write a null-terminated string to a stream \(encoding it when UTF-8
+support is enabled.)."
+  (declare (type stream socket)
+           (type string string)
+           #.*optimize*)
+  (enc-write-string string socket)
+  (write-uint1 socket 0))
+
+(declaim (ftype (function (t unsigned-byte)
+                          (simple-array (unsigned-byte 8) (*)))
+                read-bytes))
+(defun read-bytes (socket length)
+  "Read a byte array of the given length from a stream."
+  (declare (type stream socket)
+           (type fixnum length)
+           #.*optimize*)
+  (let ((result (make-array length :element-type '(unsigned-byte 8))))
+    (read-sequence result socket)
+    result))
+
+(declaim (ftype (function (t) string) read-str))
+(defun read-str (socket)
+  "Read a null-terminated string from a stream. Takes care of encoding
+when UTF-8 support is enabled."
+  (declare (type stream socket)
+           #.*optimize*)
+  (enc-read-string socket :null-terminated t))
+
+(defun skip-bytes (socket length)
+  "Skip a given number of bytes in a binary stream."
+  (declare (type stream socket)
+           (type (unsigned-byte 32) length)
+           #.*optimize*)
+  (dotimes (i length)
+    (read-byte socket)))
+
+(defun skip-str (socket)
+  "Skip a null-terminated string."
+  (declare (type stream socket)
+           #.*optimize*)
+  (loop :for char :of-type fixnum = (read-byte socket)
+        :until (zerop char)))
+
+(defun ensure-socket-is-closed (socket &amp;key abort)
+  (when (open-stream-p socket)
+    (handler-case
+        (close socket :abort abort)
+      (error (error)
+        (warn "Ignoring the error which happened while trying to close PostgreSQL socket: ~A" error)))))
+</textarea></form>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {lineNumbers: true});
+    </script>
+
+    <p><strong>MIME types defined:</strong> <code>text/x-common-lisp</code>.</p>
+
+  </article>

+ 482 - 0
libs/codemirror/mode/crystal/crystal.js

@@ -0,0 +1,482 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+;(function (mod) {
+    if (typeof exports == 'object' && typeof module == 'object')
+        // CommonJS
+        mod(require('../../lib/codemirror'))
+    else if (typeof define == 'function' && define.amd)
+        // AMD
+        define(['../../lib/codemirror'], mod)
+    // Plain browser env
+    else mod(CodeMirror)
+})(function (CodeMirror) {
+    'use strict'
+
+    CodeMirror.defineMode('crystal', function (config) {
+        function wordRegExp(words, end) {
+            return new RegExp((end ? '' : '^') + '(?:' + words.join('|') + ')' + (end ? '$' : '\\b'))
+        }
+
+        function chain(tokenize, stream, state) {
+            state.tokenize.push(tokenize)
+            return tokenize(stream, state)
+        }
+
+        var operators = /^(?:[-+/%|&^]|\*\*?|[<>]{2})/
+        var conditionalOperators = /^(?:[=!]~|===|<=>|[<>=!]=?|[|&]{2}|~)/
+        var indexingOperators = /^(?:\[\][?=]?)/
+        var anotherOperators = /^(?:\.(?:\.{2})?|->|[?:])/
+        var idents = /^[a-z_\u009F-\uFFFF][a-zA-Z0-9_\u009F-\uFFFF]*/
+        var types = /^[A-Z_\u009F-\uFFFF][a-zA-Z0-9_\u009F-\uFFFF]*/
+        var keywords = wordRegExp([
+            'abstract',
+            'alias',
+            'as',
+            'asm',
+            'begin',
+            'break',
+            'case',
+            'class',
+            'def',
+            'do',
+            'else',
+            'elsif',
+            'end',
+            'ensure',
+            'enum',
+            'extend',
+            'for',
+            'fun',
+            'if',
+            'include',
+            'instance_sizeof',
+            'lib',
+            'macro',
+            'module',
+            'next',
+            'of',
+            'out',
+            'pointerof',
+            'private',
+            'protected',
+            'rescue',
+            'return',
+            'require',
+            'select',
+            'sizeof',
+            'struct',
+            'super',
+            'then',
+            'type',
+            'typeof',
+            'uninitialized',
+            'union',
+            'unless',
+            'until',
+            'when',
+            'while',
+            'with',
+            'yield',
+            '__DIR__',
+            '__END_LINE__',
+            '__FILE__',
+            '__LINE__',
+        ])
+        var atomWords = wordRegExp(['true', 'false', 'nil', 'self'])
+        var indentKeywordsArray = ['def', 'fun', 'macro', 'class', 'module', 'struct', 'lib', 'enum', 'union', 'do', 'for']
+        var indentKeywords = wordRegExp(indentKeywordsArray)
+        var indentExpressionKeywordsArray = ['if', 'unless', 'case', 'while', 'until', 'begin', 'then']
+        var indentExpressionKeywords = wordRegExp(indentExpressionKeywordsArray)
+        var dedentKeywordsArray = ['end', 'else', 'elsif', 'rescue', 'ensure']
+        var dedentKeywords = wordRegExp(dedentKeywordsArray)
+        var dedentPunctualsArray = ['\\)', '\\}', '\\]']
+        var dedentPunctuals = new RegExp('^(?:' + dedentPunctualsArray.join('|') + ')$')
+        var nextTokenizer = {
+            def: tokenFollowIdent,
+            fun: tokenFollowIdent,
+            macro: tokenMacroDef,
+            class: tokenFollowType,
+            module: tokenFollowType,
+            struct: tokenFollowType,
+            lib: tokenFollowType,
+            enum: tokenFollowType,
+            union: tokenFollowType,
+        }
+        var matching = { '[': ']', '{': '}', '(': ')', '<': '>' }
+
+        function tokenBase(stream, state) {
+            if (stream.eatSpace()) {
+                return null
+            }
+
+            // Macros
+            if (state.lastToken != '\\' && stream.match('{%', false)) {
+                return chain(tokenMacro('%', '%'), stream, state)
+            }
+
+            if (state.lastToken != '\\' && stream.match('{{', false)) {
+                return chain(tokenMacro('{', '}'), stream, state)
+            }
+
+            // Comments
+            if (stream.peek() == '#') {
+                stream.skipToEnd()
+                return 'comment'
+            }
+
+            // Variables and keywords
+            var matched
+            if (stream.match(idents)) {
+                stream.eat(/[?!]/)
+
+                matched = stream.current()
+                if (stream.eat(':')) {
+                    return 'atom'
+                } else if (state.lastToken == '.') {
+                    return 'property'
+                } else if (keywords.test(matched)) {
+                    if (indentKeywords.test(matched)) {
+                        if (!(matched == 'fun' && state.blocks.indexOf('lib') >= 0) && !(matched == 'def' && state.lastToken == 'abstract')) {
+                            state.blocks.push(matched)
+                            state.currentIndent += 1
+                        }
+                    } else if ((state.lastStyle == 'operator' || !state.lastStyle) && indentExpressionKeywords.test(matched)) {
+                        state.blocks.push(matched)
+                        state.currentIndent += 1
+                    } else if (matched == 'end') {
+                        state.blocks.pop()
+                        state.currentIndent -= 1
+                    }
+
+                    if (nextTokenizer.hasOwnProperty(matched)) {
+                        state.tokenize.push(nextTokenizer[matched])
+                    }
+
+                    return 'keyword'
+                } else if (atomWords.test(matched)) {
+                    return 'atom'
+                }
+
+                return 'variable'
+            }
+
+            // Class variables and instance variables
+            // or attributes
+            if (stream.eat('@')) {
+                if (stream.peek() == '[') {
+                    return chain(tokenNest('[', ']', 'meta'), stream, state)
+                }
+
+                stream.eat('@')
+                stream.match(idents) || stream.match(types)
+                return 'variable-2'
+            }
+
+            // Constants and types
+            if (stream.match(types)) {
+                return 'tag'
+            }
+
+            // Symbols or ':' operator
+            if (stream.eat(':')) {
+                if (stream.eat('"')) {
+                    return chain(tokenQuote('"', 'atom', false), stream, state)
+                } else if (stream.match(idents) || stream.match(types) || stream.match(operators) || stream.match(conditionalOperators) || stream.match(indexingOperators)) {
+                    return 'atom'
+                }
+                stream.eat(':')
+                return 'operator'
+            }
+
+            // Strings
+            if (stream.eat('"')) {
+                return chain(tokenQuote('"', 'string', true), stream, state)
+            }
+
+            // Strings or regexps or macro variables or '%' operator
+            if (stream.peek() == '%') {
+                var style = 'string'
+                var embed = true
+                var delim
+
+                if (stream.match('%r')) {
+                    // Regexps
+                    style = 'string-2'
+                    delim = stream.next()
+                } else if (stream.match('%w')) {
+                    embed = false
+                    delim = stream.next()
+                } else if (stream.match('%q')) {
+                    embed = false
+                    delim = stream.next()
+                } else {
+                    if ((delim = stream.match(/^%([^\w\s=])/))) {
+                        delim = delim[1]
+                    } else if (stream.match(/^%[a-zA-Z_\u009F-\uFFFF][\w\u009F-\uFFFF]*/)) {
+                        // Macro variables
+                        return 'meta'
+                    } else if (stream.eat('%')) {
+                        // '%' operator
+                        return 'operator'
+                    }
+                }
+
+                if (matching.hasOwnProperty(delim)) {
+                    delim = matching[delim]
+                }
+                return chain(tokenQuote(delim, style, embed), stream, state)
+            }
+
+            // Here Docs
+            if ((matched = stream.match(/^<<-('?)([A-Z]\w*)\1/))) {
+                return chain(tokenHereDoc(matched[2], !matched[1]), stream, state)
+            }
+
+            // Characters
+            if (stream.eat("'")) {
+                stream.match(/^(?:[^']|\\(?:[befnrtv0'"]|[0-7]{3}|u(?:[0-9a-fA-F]{4}|\{[0-9a-fA-F]{1,6}\})))/)
+                stream.eat("'")
+                return 'atom'
+            }
+
+            // Numbers
+            if (stream.eat('0')) {
+                if (stream.eat('x')) {
+                    stream.match(/^[0-9a-fA-F_]+/)
+                } else if (stream.eat('o')) {
+                    stream.match(/^[0-7_]+/)
+                } else if (stream.eat('b')) {
+                    stream.match(/^[01_]+/)
+                }
+                return 'number'
+            }
+
+            if (stream.eat(/^\d/)) {
+                stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+-]?\d+)?/)
+                return 'number'
+            }
+
+            // Operators
+            if (stream.match(operators)) {
+                stream.eat('=') // Operators can follow assign symbol.
+                return 'operator'
+            }
+
+            if (stream.match(conditionalOperators) || stream.match(anotherOperators)) {
+                return 'operator'
+            }
+
+            // Parens and braces
+            if ((matched = stream.match(/[({[]/, false))) {
+                matched = matched[0]
+                return chain(tokenNest(matched, matching[matched], null), stream, state)
+            }
+
+            // Escapes
+            if (stream.eat('\\')) {
+                stream.next()
+                return 'meta'
+            }
+
+            stream.next()
+            return null
+        }
+
+        function tokenNest(begin, end, style, started) {
+            return function (stream, state) {
+                if (!started && stream.match(begin)) {
+                    state.tokenize[state.tokenize.length - 1] = tokenNest(begin, end, style, true)
+                    state.currentIndent += 1
+                    return style
+                }
+
+                var nextStyle = tokenBase(stream, state)
+                if (stream.current() === end) {
+                    state.tokenize.pop()
+                    state.currentIndent -= 1
+                    nextStyle = style
+                }
+
+                return nextStyle
+            }
+        }
+
+        function tokenMacro(begin, end, started) {
+            return function (stream, state) {
+                if (!started && stream.match('{' + begin)) {
+                    state.currentIndent += 1
+                    state.tokenize[state.tokenize.length - 1] = tokenMacro(begin, end, true)
+                    return 'meta'
+                }
+
+                if (stream.match(end + '}')) {
+                    state.currentIndent -= 1
+                    state.tokenize.pop()
+                    return 'meta'
+                }
+
+                return tokenBase(stream, state)
+            }
+        }
+
+        function tokenMacroDef(stream, state) {
+            if (stream.eatSpace()) {
+                return null
+            }
+
+            var matched
+            if ((matched = stream.match(idents))) {
+                if (matched == 'def') {
+                    return 'keyword'
+                }
+                stream.eat(/[?!]/)
+            }
+
+            state.tokenize.pop()
+            return 'def'
+        }
+
+        function tokenFollowIdent(stream, state) {
+            if (stream.eatSpace()) {
+                return null
+            }
+
+            if (stream.match(idents)) {
+                stream.eat(/[!?]/)
+            } else {
+                stream.match(operators) || stream.match(conditionalOperators) || stream.match(indexingOperators)
+            }
+            state.tokenize.pop()
+            return 'def'
+        }
+
+        function tokenFollowType(stream, state) {
+            if (stream.eatSpace()) {
+                return null
+            }
+
+            stream.match(types)
+            state.tokenize.pop()
+            return 'def'
+        }
+
+        function tokenQuote(end, style, embed) {
+            return function (stream, state) {
+                var escaped = false
+
+                while (stream.peek()) {
+                    if (!escaped) {
+                        if (stream.match('{%', false)) {
+                            state.tokenize.push(tokenMacro('%', '%'))
+                            return style
+                        }
+
+                        if (stream.match('{{', false)) {
+                            state.tokenize.push(tokenMacro('{', '}'))
+                            return style
+                        }
+
+                        if (embed && stream.match('#{', false)) {
+                            state.tokenize.push(tokenNest('#{', '}', 'meta'))
+                            return style
+                        }
+
+                        var ch = stream.next()
+
+                        if (ch == end) {
+                            state.tokenize.pop()
+                            return style
+                        }
+
+                        escaped = embed && ch == '\\'
+                    } else {
+                        stream.next()
+                        escaped = false
+                    }
+                }
+
+                return style
+            }
+        }
+
+        function tokenHereDoc(phrase, embed) {
+            return function (stream, state) {
+                if (stream.sol()) {
+                    stream.eatSpace()
+                    if (stream.match(phrase)) {
+                        state.tokenize.pop()
+                        return 'string'
+                    }
+                }
+
+                var escaped = false
+                while (stream.peek()) {
+                    if (!escaped) {
+                        if (stream.match('{%', false)) {
+                            state.tokenize.push(tokenMacro('%', '%'))
+                            return 'string'
+                        }
+
+                        if (stream.match('{{', false)) {
+                            state.tokenize.push(tokenMacro('{', '}'))
+                            return 'string'
+                        }
+
+                        if (embed && stream.match('#{', false)) {
+                            state.tokenize.push(tokenNest('#{', '}', 'meta'))
+                            return 'string'
+                        }
+
+                        escaped = embed && stream.next() == '\\'
+                    } else {
+                        stream.next()
+                        escaped = false
+                    }
+                }
+
+                return 'string'
+            }
+        }
+
+        return {
+            startState: function () {
+                return {
+                    tokenize: [tokenBase],
+                    currentIndent: 0,
+                    lastToken: null,
+                    lastStyle: null,
+                    blocks: [],
+                }
+            },
+
+            token: function (stream, state) {
+                var style = state.tokenize[state.tokenize.length - 1](stream, state)
+                var token = stream.current()
+
+                if (style && style != 'comment') {
+                    state.lastToken = token
+                    state.lastStyle = style
+                }
+
+                return style
+            },
+
+            indent: function (state, textAfter) {
+                textAfter = textAfter.replace(/^\s*(?:\{%)?\s*|\s*(?:%\})?\s*$/g, '')
+
+                if (dedentKeywords.test(textAfter) || dedentPunctuals.test(textAfter)) {
+                    return config.indentUnit * (state.currentIndent - 1)
+                }
+
+                return config.indentUnit * state.currentIndent
+            },
+
+            fold: 'indent',
+            electricInput: wordRegExp(dedentPunctualsArray.concat(dedentKeywordsArray), true),
+            lineComment: '#',
+        }
+    })
+
+    CodeMirror.defineMIME('text/x-crystal', 'crystal')
+})

+ 0 - 0
libs/codemirror/mode/crystal/index.html


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor