Browse Source

add codemirror and update pythoncode question type

pull/96/head
adam j hartz 1 week ago
parent
commit
8516d30e31
100 changed files with 33878 additions and 87 deletions
  1. +9
    -2
      CHANGELOG.md
  2. +23
    -27
      LICENSE.bundled_software
  3. +6
    -13
      catsoop/__HANDLERS__/default/default.py
  4. +25
    -45
      catsoop/__QTYPES__/pythoncode/pythoncode.py
  5. +349
    -0
      catsoop/__STATIC__/scripts/codemirror/codemirror.css
  6. +9778
    -0
      catsoop/__STATIC__/scripts/codemirror/codemirror.js
  7. +419
    -0
      catsoop/__STATIC__/scripts/codemirror/keymap/emacs.js
  8. +714
    -0
      catsoop/__STATIC__/scripts/codemirror/keymap/sublime.js
  9. +5545
    -0
      catsoop/__STATIC__/scripts/codemirror/keymap/vim.js
  10. +174
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/apl/apl.js
  11. +72
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/apl/index.html
  12. +74
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/asciiarmor/asciiarmor.js
  13. +46
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/asciiarmor/index.html
  14. +204
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/asn.1/asn.1.js
  15. +78
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/asn.1/index.html
  16. +220
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/asterisk/asterisk.js
  17. +155
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/asterisk/index.html
  18. +85
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/brainfuck/brainfuck.js
  19. +85
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/brainfuck/index.html
  20. +935
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/clike/clike.js
  21. +380
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/clike/index.html
  22. +767
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/clike/scala.html
  23. +165
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/clike/test.js
  24. +292
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/clojure/clojure.js
  25. +95
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/clojure/index.html
  26. +384
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/clojure/test.js
  27. +97
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/cmake/cmake.js
  28. +129
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/cmake/index.html
  29. +255
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/cobol/cobol.js
  30. +210
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/cobol/index.html
  31. +359
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/coffeescript/coffeescript.js
  32. +740
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/coffeescript/index.html
  33. +124
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/commonlisp/commonlisp.js
  34. +177
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/commonlisp/index.html
  35. +433
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/crystal/crystal.js
  36. +116
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/crystal/index.html
  37. +860
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/css.js
  38. +104
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/gss.html
  39. +17
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/gss_test.js
  40. +75
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/index.html
  41. +152
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/less.html
  42. +54
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/less_test.js
  43. +158
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/scss.html
  44. +110
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/scss_test.js
  45. +217
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/css/test.js
  46. +151
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/cypher/cypher.js
  47. +64
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/cypher/index.html
  48. +37
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/cypher/test.js
  49. +223
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/d/d.js
  50. +273
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/d/index.html
  51. +11
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/d/test.js
  52. +166
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dart/dart.js
  53. +71
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dart/index.html
  54. +47
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/diff/diff.js
  55. +117
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/diff/index.html
  56. +356
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/django/django.js
  57. +73
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/django/index.html
  58. +211
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dockerfile/dockerfile.js
  59. +73
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dockerfile/index.html
  60. +128
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dockerfile/test.js
  61. +142
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dtd/dtd.js
  62. +89
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dtd/index.html
  63. +352
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dylan/dylan.js
  64. +407
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dylan/index.html
  65. +88
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/dylan/test.js
  66. +195
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/ebnf/ebnf.js
  67. +102
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/ebnf/index.html
  68. +206
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/ecl/ecl.js
  69. +52
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/ecl/index.html
  70. +160
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/eiffel/eiffel.js
  71. +429
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/eiffel/index.html
  72. +243
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/elm/elm.js
  73. +61
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/elm/index.html
  74. +619
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/erlang/erlang.js
  75. +76
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/erlang/index.html
  76. +85
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/factor/factor.js
  77. +77
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/factor/index.html
  78. +173
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/fcl/fcl.js
  79. +108
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/fcl/index.html
  80. +180
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/forth/forth.js
  81. +75
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/forth/index.html
  82. +188
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/fortran/fortran.js
  83. +81
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/fortran/index.html
  84. +345
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/gas/gas.js
  85. +68
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/gas/index.html
  86. +129
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/gfm/gfm.js
  87. +136
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/gfm/index.html
  88. +198
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/gfm/test.js
  89. +178
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/gherkin/gherkin.js
  90. +48
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/gherkin/index.html
  91. +187
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/go/go.js
  92. +85
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/go/index.html
  93. +233
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/groovy/groovy.js
  94. +84
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/groovy/index.html
  95. +161
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/haml/haml.js
  96. +79
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/haml/index.html
  97. +97
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/haml/test.js
  98. +70
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/handlebars/handlebars.js
  99. +82
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/handlebars/index.html
  100. +43
    -0
      catsoop/__STATIC__/scripts/codemirror/mode/haskell-literate/haskell-literate.js

+ 9
- 2
CHANGELOG.md View File

@@ -23,8 +23,8 @@ _Work toward next release. Currently under development._
* Added the ability to customize the text shown on the button in `<showhide>`
tags, via the summary attribute

* Added support for Markdown "callouts", similar to [Markdeep's
"admonitions"](https://casual-effects.com/markdeep/features.md.html#basicformatting/admonitions)
* Added support for Markdown "callouts", similar to Markdeep's
["admonitions"](https://casual-effects.com/markdeep/features.md.html#basicformatting/admonitions)
(#90)

**CHANGED:**
@@ -47,6 +47,13 @@ _Work toward next release. Currently under development._
a conflict with X11, and modified the `catsoop configure` command to ask
about the port numbers (#83 and #84)

* Replaced `js_files` in question types with `extra_headers`, which allows
injecting arbitrary strings into the header (which allows specifying not
only Javascript files but also stylesheets)

* Removed Ace editor in favor of [CodeMirror](https://codemirror.net/) as the
default web-based code editor (#96)

**DEPRECATED:**

**REMOVED:**


+ 23
- 27
LICENSE.bundled_software View File

@@ -6,34 +6,30 @@ those pieces of software, separated by the string ######
########


The CAT-SOOP distribution contains a verbatim copy of Ace, a web-based code
editor (https://ace.c9.io/) in the __STATIC__/scripts/ace directory. Ace is
licensed according to the following terms:
The CAT-SOOP distribution contains a verbatim copy of pieces of CodeMirror, a
web-based code editor (https://codemirror.net/) in the
__STATIC__/scripts/codemirror directory. CodeMirror is licensed according to
the following terms:

Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others

Copyright (c) 2010, Ajax.org B.V.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Ajax.org B.V. nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


########


+ 6
- 13
catsoop/__HANDLERS__/default/default.py View File

@@ -412,7 +412,7 @@ def handle_view(context):
"<br/>&nbsp;<br/></center></tutoronly>"
) % duetime

js_loads = []
extra_headers = set()
for elt in context["cs_problem_spec"]:
if isinstance(elt, str):
page += elt
@@ -421,20 +421,13 @@ def handle_view(context):
page += render_question(elt, context, lastsubmit)

# handle javascript if necessary
if "js_files" in elt[0]:
if "extra_headers" in elt[0]:
a = elt[0].get("defaults", {})
a.update(elt[1])
js_loads.extend(elt[0]["js_files"](a))

if js_loads:
context["cs_scripts"] += (
"\n\n <!--JS for questions-->\n "
+ "\n ".join(
'<script type="text/javascript" src="%s"></script>'
% context["csm_dispatch"].get_real_url(context, i)
for i in js_loads
)
)
extra_headers.add(elt[0]["extra_headers"](a))

if extra_headers:
context["cs_scripts"] += "\n".join(extra_headers)

page += default_javascript(context)
page += default_timer(context)


+ 25
- 45
catsoop/__QTYPES__/pythoncode/pythoncode.py View File

@@ -43,11 +43,19 @@ def get_sandbox(context):
_execfile(base, context)


def js_files(info):
if info["csq_interface"] == "ace":
return ["BASE/scripts/ace/ace.js"]
SCRIPTS = """<!-- CodeMirror -->
<script type="text/javascript" src="BASE/scripts/codemirror/codemirror.js"></script>
<script type="text/javascript" src="BASE/scripts/codemirror/mode/python/python.js"></script>
<link rel="stylesheet" href="BASE/scripts/codemirror/codemirror.css" />

"""


def extra_headers(info):
if info["csq_interface"] == "codemirror":
return SCRIPTS
else:
return []
return None


def html_format(string):
@@ -82,7 +90,7 @@ defaults = {
"csq_cpu_limit": 2,
"csq_nproc_limit": 0,
"csq_memory_limit": 32e6,
"csq_interface": "ace",
"csq_interface": "codemirror",
"csq_rows": 14,
"csq_font_size": 16,
"csq_always_show_tests": False,
@@ -591,57 +599,29 @@ def render_html_upload(last_log, **info):
return out


def render_html_ace(last_log, **info):
def render_html_codemirror(last_log, **info):
name = info["csq_name"]
init = last_log.get(name, None)
if init is None:
init = make_initial_display(info)
init = str(init)
fontsize = info["csq_font_size"]
params = {
"name": name,
"init": init,
"safeinit": init.replace("<", "&lt;"),
"height": info["csq_rows"] * (fontsize + 4),
"fontsize": fontsize,
}

return (
"""
<div class="ace_editor_wrapper" id="container%(name)s">
<div id="editor%(name)s" name="editor%(name)s" class="embedded_ace_code">%(safeinit)s</div></div>
<input type="hidden" name="%(name)s" id="%(name)s" />
<input type="hidden" name="%(name)s_log" id="%(name)s_log" />
<script type="text/javascript">
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3
var log%(name)s = new Array();
var editor%(name)s = ace.edit("editor%(name)s");
editor%(name)s.setTheme("ace/theme/textmate");
editor%(name)s.getSession().setMode("ace/mode/python");
editor%(name)s.setShowFoldWidgets(false);
editor%(name)s.setValue(%(init)r)
document.getElementById("%(name)s").value = editor%(name)s.getValue();
editor%(name)s.on("change",function(e){
editor%(name)s.getSession().setUseSoftTabs(true);
document.getElementById("%(name)s").value = editor%(name)s.getValue();
});
editor%(name)s.clearSelection()
editor%(name)s.getSession().setUseSoftTabs(true);
editor%(name)s.on("paste",function(txt){editor%(name)s.getSession().setUseSoftTabs(false);});
editor%(name)s.getSession().setTabSize(4);
editor%(name)s.setFontSize("%(fontsize)spx");
document.getElementById("container%(name)s").style.height = "%(height)spx";
document.getElementById("editor%(name)s").style.height = "%(height)spx";
editor%(name)s.resize(true);
// @license-end
</script>"""
% params
f'\n<textarea name="{name}" id="{name}">{init}</textarea>'
'\n<script type="text/javascript">'
f"\nvar cs_codemirror_{name} = CodeMirror.fromTextArea(document.getElementById('{name}'), {{"
"\n lineNumbers: true,"
"\n indentUnit: 4,"
"\n});"
f"\ncs_codemirror_{name}.on('change', function(){{"
f"\n cs_codemirror_{name}.save();"
"\n});"
"\n</script>"
)


RENDERERS = {
"textarea": render_html_textarea,
"ace": render_html_ace,
"codemirror": render_html_codemirror,
"upload": render_html_upload,
}



+ 349
- 0
catsoop/__STATIC__/scripts/codemirror/codemirror.css View File

@@ -0,0 +1,349 @@
/* 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-mark {
background-color: rgba(20, 255, 20, 0.5);
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
}
.cm-animate-fat-cursor {
width: auto;
border: 0;
-webkit-animation: blink 1.06s steps(1) infinite;
-moz-animation: blink 1.06s steps(1) infinite;
animation: blink 1.06s steps(1) infinite;
background-color: #7e7;
}
@-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;
}
.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;
}
.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; }

+ 9778
- 0
catsoop/__STATIC__/scripts/codemirror/codemirror.js
File diff suppressed because it is too large
View File


+ 419
- 0
catsoop/__STATIC__/scripts/codemirror/keymap/emacs.js View File

@@ -0,0 +1,419 @@
// 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);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

var Pos = CodeMirror.Pos;
function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }

// Kill 'ring'

var killRing = [];
function addToRing(str) {
killRing.push(str);
if (killRing.length > 50) killRing.shift();
}
function growRingTop(str) {
if (!killRing.length) return addToRing(str);
killRing[killRing.length - 1] += str;
}
function getFromRing(n) { return killRing[killRing.length - (n ? Math.min(n, 1) : 1)] || ""; }
function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }

var lastKill = null;

function kill(cm, from, to, ring, text) {
if (text == null) text = cm.getRange(from, to);

if (ring == "grow" && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))
growRingTop(text);
else if (ring !== false)
addToRing(text);
cm.replaceRange("", from, to, "+delete");

if (ring == "grow") lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};
else lastKill = null;
}

// Boundaries of various units

function byChar(cm, pos, dir) {
return cm.findPosH(pos, dir, "char", true);
}

function byWord(cm, pos, dir) {
return cm.findPosH(pos, dir, "word", true);
}

function byLine(cm, pos, dir) {
return cm.findPosV(pos, dir, "line", cm.doc.sel.goalColumn);
}

function byPage(cm, pos, dir) {
return cm.findPosV(pos, dir, "page", cm.doc.sel.goalColumn);
}

function byParagraph(cm, pos, dir) {
var no = pos.line, line = cm.getLine(no);
var sawText = /\S/.test(dir < 0 ? line.slice(0, pos.ch) : line.slice(pos.ch));
var fst = cm.firstLine(), lst = cm.lastLine();
for (;;) {
no += dir;
if (no < fst || no > lst)
return cm.clipPos(Pos(no - dir, dir < 0 ? 0 : null));
line = cm.getLine(no);
var hasText = /\S/.test(line);
if (hasText) sawText = true;
else if (sawText) return Pos(no, 0);
}
}

function bySentence(cm, pos, dir) {
var line = pos.line, ch = pos.ch;
var text = cm.getLine(pos.line), sawWord = false;
for (;;) {
var next = text.charAt(ch + (dir < 0 ? -1 : 0));
if (!next) { // End/beginning of line reached
if (line == (dir < 0 ? cm.firstLine() : cm.lastLine())) return Pos(line, ch);
text = cm.getLine(line + dir);
if (!/\S/.test(text)) return Pos(line, ch);
line += dir;
ch = dir < 0 ? text.length : 0;
continue;
}
if (sawWord && /[!?.]/.test(next)) return Pos(line, ch + (dir > 0 ? 1 : 0));
if (!sawWord) sawWord = /\w/.test(next);
ch += dir;
}
}

function byExpr(cm, pos, dir) {
var wrap;
if (cm.findMatchingBracket && (wrap = cm.findMatchingBracket(pos, {strict: true}))
&& wrap.match && (wrap.forward ? 1 : -1) == dir)
return dir > 0 ? Pos(wrap.to.line, wrap.to.ch + 1) : wrap.to;

for (var first = true;; first = false) {
var token = cm.getTokenAt(pos);
var after = Pos(pos.line, dir < 0 ? token.start : token.end);
if (first && dir > 0 && token.end == pos.ch || !/\w/.test(token.string)) {
var newPos = cm.findPosH(after, dir, "char");
if (posEq(after, newPos)) return pos;
else pos = newPos;
} else {
return after;
}
}
}

// Prefixes (only crudely supported)

function getPrefix(cm, precise) {
var digits = cm.state.emacsPrefix;
if (!digits) return precise ? null : 1;
clearPrefix(cm);
return digits == "-" ? -1 : Number(digits);
}

function repeated(cmd) {
var f = typeof cmd == "string" ? function(cm) { cm.execCommand(cmd); } : cmd;
return function(cm) {
var prefix = getPrefix(cm);
f(cm);
for (var i = 1; i < prefix; ++i) f(cm);
};
}

function findEnd(cm, pos, by, dir) {
var prefix = getPrefix(cm);
if (prefix < 0) { dir = -dir; prefix = -prefix; }
for (var i = 0; i < prefix; ++i) {
var newPos = by(cm, pos, dir);
if (posEq(newPos, pos)) break;
pos = newPos;
}
return pos;
}

function move(by, dir) {
var f = function(cm) {
cm.extendSelection(findEnd(cm, cm.getCursor(), by, dir));
};
f.motion = true;
return f;
}

function killTo(cm, by, dir, ring) {
var selections = cm.listSelections(), cursor;
var i = selections.length;
while (i--) {
cursor = selections[i].head;
kill(cm, cursor, findEnd(cm, cursor, by, dir), ring);
}
}

function killRegion(cm, ring) {
if (cm.somethingSelected()) {
var selections = cm.listSelections(), selection;
var i = selections.length;
while (i--) {
selection = selections[i];
kill(cm, selection.anchor, selection.head, ring);
}
return true;
}
}

function addPrefix(cm, digit) {
if (cm.state.emacsPrefix) {
if (digit != "-") cm.state.emacsPrefix += digit;
return;
}
// Not active yet
cm.state.emacsPrefix = digit;
cm.on("keyHandled", maybeClearPrefix);
cm.on("inputRead", maybeDuplicateInput);
}

var prefixPreservingKeys = {"Alt-G": true, "Ctrl-X": true, "Ctrl-Q": true, "Ctrl-U": true};

function maybeClearPrefix(cm, arg) {
if (!cm.state.emacsPrefixMap && !prefixPreservingKeys.hasOwnProperty(arg))
clearPrefix(cm);
}

function clearPrefix(cm) {
cm.state.emacsPrefix = null;
cm.off("keyHandled", maybeClearPrefix);
cm.off("inputRead", maybeDuplicateInput);
}

function maybeDuplicateInput(cm, event) {
var dup = getPrefix(cm);
if (dup > 1 && event.origin == "+input") {
var one = event.text.join("\n"), txt = "";
for (var i = 1; i < dup; ++i) txt += one;
cm.replaceSelection(txt);
}
}

function addPrefixMap(cm) {
cm.state.emacsPrefixMap = true;
cm.addKeyMap(prefixMap);
cm.on("keyHandled", maybeRemovePrefixMap);
cm.on("inputRead", maybeRemovePrefixMap);
}

function maybeRemovePrefixMap(cm, arg) {
if (typeof arg == "string" && (/^\d$/.test(arg) || arg == "Ctrl-U")) return;
cm.removeKeyMap(prefixMap);
cm.state.emacsPrefixMap = false;
cm.off("keyHandled", maybeRemovePrefixMap);
cm.off("inputRead", maybeRemovePrefixMap);
}

// Utilities

function setMark(cm) {
cm.setCursor(cm.getCursor());
cm.setExtending(!cm.getExtending());
cm.on("change", function() { cm.setExtending(false); });
}

function clearMark(cm) {
cm.setExtending(false);
cm.setCursor(cm.getCursor());
}

function getInput(cm, msg, f) {
if (cm.openDialog)
cm.openDialog(msg + ": <input type=\"text\" style=\"width: 10em\"/>", f, {bottom: true});
else
f(prompt(msg, ""));
}

function operateOnWord(cm, op) {
var start = cm.getCursor(), end = cm.findPosH(start, 1, "word");
cm.replaceRange(op(cm.getRange(start, end)), start, end);
cm.setCursor(end);
}

function toEnclosingExpr(cm) {
var pos = cm.getCursor(), line = pos.line, ch = pos.ch;
var stack = [];
while (line >= cm.firstLine()) {
var text = cm.getLine(line);
for (var i = ch == null ? text.length : ch; i > 0;) {
var ch = text.charAt(--i);
if (ch == ")")
stack.push("(");
else if (ch == "]")
stack.push("[");
else if (ch == "}")
stack.push("{");
else if (/[\(\{\[]/.test(ch) && (!stack.length || stack.pop() != ch))
return cm.extendSelection(Pos(line, i));
}
--line; ch = null;
}
}

function quit(cm) {
cm.execCommand("clearSearch");
clearMark(cm);
}

CodeMirror.emacs = {kill: kill, killRegion: killRegion, repeated: repeated};

// Actual keymap

var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({
"Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"), true);},
"Ctrl-K": repeated(function(cm) {
var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
var text = cm.getRange(start, end);
if (!/\S/.test(text)) {
text += "\n";
end = Pos(start.line + 1, 0);
}
kill(cm, start, end, "grow", text);
}),
"Alt-W": function(cm) {
addToRing(cm.getSelection());
clearMark(cm);
},
"Ctrl-Y": function(cm) {
var start = cm.getCursor();
cm.replaceRange(getFromRing(getPrefix(cm)), start, start, "paste");
cm.setSelection(start, cm.getCursor());
},
"Alt-Y": function(cm) {cm.replaceSelection(popFromRing(), "around", "paste");},

"Ctrl-Space": setMark, "Ctrl-Shift-2": setMark,

"Ctrl-F": move(byChar, 1), "Ctrl-B": move(byChar, -1),
"Right": move(byChar, 1), "Left": move(byChar, -1),
"Ctrl-D": function(cm) { killTo(cm, byChar, 1, false); },
"Delete": function(cm) { killRegion(cm, false) || killTo(cm, byChar, 1, false); },
"Ctrl-H": function(cm) { killTo(cm, byChar, -1, false); },
"Backspace": function(cm) { killRegion(cm, false) || killTo(cm, byChar, -1, false); },

"Alt-F": move(byWord, 1), "Alt-B": move(byWord, -1),
"Alt-Right": move(byWord, 1), "Alt-Left": move(byWord, -1),
"Alt-D": function(cm) { killTo(cm, byWord, 1, "grow"); },
"Alt-Backspace": function(cm) { killTo(cm, byWord, -1, "grow"); },

"Ctrl-N": move(byLine, 1), "Ctrl-P": move(byLine, -1),
"Down": move(byLine, 1), "Up": move(byLine, -1),
"Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
"End": "goLineEnd", "Home": "goLineStart",

"Alt-V": move(byPage, -1), "Ctrl-V": move(byPage, 1),
"PageUp": move(byPage, -1), "PageDown": move(byPage, 1),

"Ctrl-Up": move(byParagraph, -1), "Ctrl-Down": move(byParagraph, 1),

"Alt-A": move(bySentence, -1), "Alt-E": move(bySentence, 1),
"Alt-K": function(cm) { killTo(cm, bySentence, 1, "grow"); },

"Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1, "grow"); },
"Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1, "grow"); },
"Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1, "grow"),

"Shift-Ctrl-Alt-2": function(cm) {
var cursor = cm.getCursor();
cm.setSelection(findEnd(cm, cursor, byExpr, 1), cursor);
},
"Ctrl-Alt-T": function(cm) {
var leftStart = byExpr(cm, cm.getCursor(), -1), leftEnd = byExpr(cm, leftStart, 1);
var rightEnd = byExpr(cm, leftEnd, 1), rightStart = byExpr(cm, rightEnd, -1);
cm.replaceRange(cm.getRange(rightStart, rightEnd) + cm.getRange(leftEnd, rightStart) +
cm.getRange(leftStart, leftEnd), leftStart, rightEnd);
},
"Ctrl-Alt-U": repeated(toEnclosingExpr),

"Alt-Space": function(cm) {
var pos = cm.getCursor(), from = pos.ch, to = pos.ch, text = cm.getLine(pos.line);
while (from && /\s/.test(text.charAt(from - 1))) --from;
while (to < text.length && /\s/.test(text.charAt(to))) ++to;
cm.replaceRange(" ", Pos(pos.line, from), Pos(pos.line, to));
},
"Ctrl-O": repeated(function(cm) { cm.replaceSelection("\n", "start"); }),
"Ctrl-T": repeated(function(cm) {
cm.execCommand("transposeChars");
}),

"Alt-C": repeated(function(cm) {
operateOnWord(cm, function(w) {
var letter = w.search(/\w/);
if (letter == -1) return w;
return w.slice(0, letter) + w.charAt(letter).toUpperCase() + w.slice(letter + 1).toLowerCase();
});
}),
"Alt-U": repeated(function(cm) {
operateOnWord(cm, function(w) { return w.toUpperCase(); });
}),
"Alt-L": repeated(function(cm) {
operateOnWord(cm, function(w) { return w.toLowerCase(); });
}),

"Alt-;": "toggleComment",

"Ctrl-/": repeated("undo"), "Shift-Ctrl--": repeated("undo"),
"Ctrl-Z": repeated("undo"), "Cmd-Z": repeated("undo"),
"Shift-Ctrl-Z": "redo",
"Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
"Ctrl-S": "findPersistentNext", "Ctrl-R": "findPersistentPrev", "Ctrl-G": quit, "Shift-Alt-5": "replace",
"Alt-/": "autocomplete",
"Enter": "newlineAndIndent",
"Ctrl-J": repeated(function(cm) { cm.replaceSelection("\n", "end"); }),
"Tab": "indentAuto",

"Alt-G G": function(cm) {
var prefix = getPrefix(cm, true);
if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1);

getInput(cm, "Goto line", function(str) {
var num;
if (str && !isNaN(num = Number(str)) && num == (num|0) && num > 0)
cm.setCursor(num - 1);
});
},

"Ctrl-X Tab": function(cm) {
cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
},
"Ctrl-X Ctrl-X": function(cm) {
cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
},
"Ctrl-X Ctrl-S": "save",
"Ctrl-X Ctrl-W": "save",
"Ctrl-X S": "saveAll",
"Ctrl-X F": "open",
"Ctrl-X U": repeated("undo"),
"Ctrl-X K": "close",
"Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), "grow"); },
"Ctrl-X H": "selectAll",

"Ctrl-Q Tab": repeated("insertTab"),
"Ctrl-U": addPrefixMap,
"fallthrough": "default"
});

var prefixMap = {"Ctrl-G": clearPrefix};
function regPrefix(d) {
prefixMap[d] = function(cm) { addPrefix(cm, d); };
keyMap["Ctrl-" + d] = function(cm) { addPrefix(cm, d); };
prefixPreservingKeys["Ctrl-" + d] = true;
}
for (var i = 0; i < 10; ++i) regPrefix(String(i));
regPrefix("-");
});

+ 714
- 0
catsoop/__STATIC__/scripts/codemirror/keymap/sublime.js View File

@@ -0,0 +1,714 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE

// A rough approximation of Sublime Text's keybindings
// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js

(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/edit/matchbrackets"));
else if (typeof define == "function" && define.amd) // AMD
define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

var cmds = CodeMirror.commands;
var Pos = CodeMirror.Pos;

// This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
function findPosSubword(doc, start, dir) {
if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
var line = doc.getLine(start.line);
if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
var state = "start", type, startPos = start.ch;
for (var pos = startPos, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
var next = line.charAt(dir < 0 ? pos - 1 : pos);
var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
if (cat == "w" && next.toUpperCase() == next) cat = "W";
if (state == "start") {
if (cat != "o") { state = "in"; type = cat; }
else startPos = pos + dir
} else if (state == "in") {
if (type != cat) {
if (type == "w" && cat == "W" && dir < 0) pos--;
if (type == "W" && cat == "w" && dir > 0) { // From uppercase to lowercase
if (pos == startPos + 1) { type = "w"; continue; }
else pos--;
}
break;
}
}
}
return Pos(start.line, pos);
}

function moveSubword(cm, dir) {
cm.extendSelectionsBy(function(range) {
if (cm.display.shift || cm.doc.extend || range.empty())
return findPosSubword(cm.doc, range.head, dir);
else
return dir < 0 ? range.from() : range.to();
});
}

cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };

cmds.scrollLineUp = function(cm) {
var info = cm.getScrollInfo();
if (!cm.somethingSelected()) {
var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
if (cm.getCursor().line >= visibleBottomLine)
cm.execCommand("goLineUp");
}
cm.scrollTo(null, info.top - cm.defaultTextHeight());
};
cmds.scrollLineDown = function(cm) {
var info = cm.getScrollInfo();
if (!cm.somethingSelected()) {
var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
if (cm.getCursor().line <= visibleTopLine)
cm.execCommand("goLineDown");
}
cm.scrollTo(null, info.top + cm.defaultTextHeight());
};

cmds.splitSelectionByLine = function(cm) {
var ranges = cm.listSelections(), lineRanges = [];
for (var i = 0; i < ranges.length; i++) {
var from = ranges[i].from(), to = ranges[i].to();
for (var line = from.line; line <= to.line; ++line)
if (!(to.line > from.line && line == to.line && to.ch == 0))
lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
head: line == to.line ? to : Pos(line)});
}
cm.setSelections(lineRanges, 0);
};

cmds.singleSelectionTop = function(cm) {
var range = cm.listSelections()[0];
cm.setSelection(range.anchor, range.head, {scroll: false});
};

cmds.selectLine = function(cm) {
var ranges = cm.listSelections(), extended = [];
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i];
extended.push({anchor: Pos(range.from().line, 0),
head: Pos(range.to().line + 1, 0)});
}
cm.setSelections(extended);
};

function insertLine(cm, above) {
if (cm.isReadOnly()) return CodeMirror.Pass
cm.operation(function() {
var len = cm.listSelections().length, newSelection = [], last = -1;
for (var i = 0; i < len; i++) {
var head = cm.listSelections()[i].head;
if (head.line <= last) continue;
var at = Pos(head.line + (above ? 0 : 1), 0);
cm.replaceRange("\n", at, null, "+insertLine");
cm.indentLine(at.line, null, true);
newSelection.push({head: at, anchor: at});
last = head.line + 1;
}
cm.setSelections(newSelection);
});
cm.execCommand("indentAuto");
}

cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };

cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };

function wordAt(cm, pos) {
var start = pos.ch, end = start, line = cm.getLine(pos.line);
while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
}

cmds.selectNextOccurrence = function(cm) {
var from = cm.getCursor("from"), to = cm.getCursor("to");
var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
if (CodeMirror.cmpPos(from, to) == 0) {
var word = wordAt(cm, from);
if (!word.word) return;
cm.setSelection(word.from, word.to);
fullWord = true;
} else {
var text = cm.getRange(from, to);
var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
var cur = cm.getSearchCursor(query, to);
var found = cur.findNext();
if (!found) {
cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
found = cur.findNext();
}
if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to())) return
cm.addSelection(cur.from(), cur.to());
}
if (fullWord)
cm.state.sublimeFindFullWord = cm.doc.sel;
};

cmds.skipAndSelectNextOccurrence = function(cm) {
var prevAnchor = cm.getCursor("anchor"), prevHead = cm.getCursor("head");
cmds.selectNextOccurrence(cm);
if (CodeMirror.cmpPos(prevAnchor, prevHead) != 0) {
cm.doc.setSelections(cm.doc.listSelections()
.filter(function (sel) {
return sel.anchor != prevAnchor || sel.head != prevHead;
}));
}
}

function addCursorToSelection(cm, dir) {
var ranges = cm.listSelections(), newRanges = [];
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i];
var newAnchor = cm.findPosV(
range.anchor, dir, "line", range.anchor.goalColumn);
var newHead = cm.findPosV(
range.head, dir, "line", range.head.goalColumn);
newAnchor.goalColumn = range.anchor.goalColumn != null ?
range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
newHead.goalColumn = range.head.goalColumn != null ?
range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
var newRange = {anchor: newAnchor, head: newHead};
newRanges.push(range);
newRanges.push(newRange);
}
cm.setSelections(newRanges);
}
cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };

function isSelectedRange(ranges, from, to) {
for (var i = 0; i < ranges.length; i++)
if (CodeMirror.cmpPos(ranges[i].from(), from) == 0 &&
CodeMirror.cmpPos(ranges[i].to(), to) == 0) return true
return false
}

var mirror = "(){}[]";
function selectBetweenBrackets(cm) {
var ranges = cm.listSelections(), newRanges = []
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
if (!opening) return false;
for (;;) {
var closing = cm.scanForBracket(pos, 1);
if (!closing) return false;
if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
opening = cm.scanForBracket(opening.pos, -1);
if (!opening) return false;
} else {
newRanges.push({anchor: startPos, head: closing.pos});
break;
}
}
pos = Pos(closing.pos.line, closing.pos.ch + 1);
}
}
cm.setSelections(newRanges);
return true;
}

cmds.selectScope = function(cm) {
selectBetweenBrackets(cm) || cm.execCommand("selectAll");
};
cmds.selectBetweenBrackets = function(cm) {
if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
};

function puncType(type) {
return !type ? null : /\bpunctuation\b/.test(type) ? type : undefined
}

cmds.goToBracket = function(cm) {
cm.extendSelectionsBy(function(range) {
var next = cm.scanForBracket(range.head, 1, puncType(cm.getTokenTypeAt(range.head)));
if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
var prev = cm.scanForBracket(range.head, -1, puncType(cm.getTokenTypeAt(Pos(range.head.line, range.head.ch + 1))));
return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
});
};

cmds.swapLineUp = function(cm) {
if (cm.isReadOnly()) return CodeMirror.Pass
var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i], from = range.from().line - 1, to = range.to().line;
newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
head: Pos(range.head.line - 1, range.head.ch)});
if (range.to().ch == 0 && !range.empty()) --to;
if (from > at) linesToMove.push(from, to);
else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
at = to;
}
cm.operation(function() {
for (var i = 0; i < linesToMove.length; i += 2) {
var from = linesToMove[i], to = linesToMove[i + 1];
var line = cm.getLine(from);
cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
if (to > cm.lastLine())
cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
else
cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
}
cm.setSelections(newSels);
cm.scrollIntoView();
});
};

cmds.swapLineDown = function(cm) {
if (cm.isReadOnly()) return CodeMirror.Pass
var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
for (var i = ranges.length - 1; i >= 0; i--) {
var range = ranges[i], from = range.to().line + 1, to = range.from().line;
if (range.to().ch == 0 && !range.empty()) from--;
if (from < at) linesToMove.push(from, to);
else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
at = to;
}
cm.operation(function() {
for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
var from = linesToMove[i], to = linesToMove[i + 1];
var line = cm.getLine(from);
if (from == cm.lastLine())
cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
else
cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
}
cm.scrollIntoView();
});
};

cmds.toggleCommentIndented = function(cm) {
cm.toggleComment({ indent: true });
}

cmds.joinLines = function(cm) {
var ranges = cm.listSelections(), joined = [];
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i], from = range.from();
var start = from.line, end = range.to().line;
while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
end = ranges[++i].to().line;
joined.push({start: start, end: end, anchor: !range.empty() && from});
}
cm.operation(function() {
var offset = 0, ranges = [];
for (var i = 0; i < joined.length; i++) {
var obj = joined[i];
var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
for (var line = obj.start; line <= obj.end; line++) {
var actual = line - offset;
if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
if (actual < cm.lastLine()) {
cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
++offset;
}
}
ranges.push({anchor: anchor || head, head: head});
}
cm.setSelections(ranges, 0);
});
};

cmds.duplicateLine = function(cm) {
cm.operation(function() {
var rangeCount = cm.listSelections().length;
for (var i = 0; i < rangeCount; i++) {
var range = cm.listSelections()[i];
if (range.empty())
cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
else
cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
}
cm.scrollIntoView();
});
};


function sortLines(cm, caseSensitive) {
if (cm.isReadOnly()) return CodeMirror.Pass
var ranges = cm.listSelections(), toSort = [], selected;
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i];
if (range.empty()) continue;
var from = range.from().line, to = range.to().line;
while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
to = ranges[++i].to().line;
if (!ranges[i].to().ch) to--;
toSort.push(from, to);
}
if (toSort.length) selected = true;
else toSort.push(cm.firstLine(), cm.lastLine());

cm.operation(function() {
var ranges = [];
for (var i = 0; i < toSort.length; i += 2) {
var from = toSort[i], to = toSort[i + 1];
var start = Pos(from, 0), end = Pos(to);
var lines = cm.getRange(start, end, false);
if (caseSensitive)
lines.sort();
else
lines.sort(function(a, b) {
var au = a.toUpperCase(), bu = b.toUpperCase();
if (au != bu) { a = au; b = bu; }
return a < b ? -1 : a == b ? 0 : 1;
});
cm.replaceRange(lines, start, end);
if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});
}
if (selected) cm.setSelections(ranges, 0);
});
}

cmds.sortLines = function(cm) { sortLines(cm, true); };
cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false); };

cmds.nextBookmark = function(cm) {
var marks = cm.state.sublimeBookmarks;
if (marks) while (marks.length) {
var current = marks.shift();
var found = current.find();
if (found) {
marks.push(current);
return cm.setSelection(found.from, found.to);
}
}
};

cmds.prevBookmark = function(cm) {
var marks = cm.state.sublimeBookmarks;
if (marks) while (marks.length) {
marks.unshift(marks.pop());
var found = marks[marks.length - 1].find();
if (!found)
marks.pop();
else
return cm.setSelection(found.from, found.to);
}
};

cmds.toggleBookmark = function(cm) {
var ranges = cm.listSelections();
var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
for (var i = 0; i < ranges.length; i++) {
var from = ranges[i].from(), to = ranges[i].to();
var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
for (var j = 0; j < found.length; j++) {
if (found[j].sublimeBookmark) {
found[j].clear();
for (var k = 0; k < marks.length; k++)
if (marks[k] == found[j])
marks.splice(k--, 1);
break;
}
}
if (j == found.length)
marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
}
};

cmds.clearBookmarks = function(cm) {
var marks = cm.state.sublimeBookmarks;
if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
marks.length = 0;
};

cmds.selectBookmarks = function(cm) {
var marks = cm.state.sublimeBookmarks, ranges = [];
if (marks) for (var i = 0; i < marks.length; i++) {
var found = marks[i].find();
if (!found)
marks.splice(i--, 0);
else
ranges.push({anchor: found.from, head: found.to});
}
if (ranges.length)
cm.setSelections(ranges, 0);
};

function modifyWordOrSelection(cm, mod) {
cm.operation(function() {
var ranges = cm.listSelections(), indices = [], replacements = [];
for (var i = 0; i < ranges.length; i++) {
var range = ranges[i];
if (range.empty()) { indices.push(i); replacements.push(""); }
else replacements.push(mod(cm.getRange(range.from(), range.to())));
}
cm.replaceSelections(replacements, "around", "case");
for (var i = indices.length - 1, at; i >= 0; i--) {
var range = ranges[indices[i]];
if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
var word = wordAt(cm, range.head);
at = word.from;
cm.replaceRange(mod(word.word), word.from, word.to);
}
});
}

cmds.smartBackspace = function(cm) {
if (cm.somethingSelected()) return CodeMirror.Pass;

cm.operation(function() {
var cursors = cm.listSelections();
var indentUnit = cm.getOption("indentUnit");

for (var i = cursors.length - 1; i >= 0; i--) {
var cursor = cursors[i].head;
var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));

// Delete by one character by default
var deletePos = cm.findPosH(cursor, -1, "char", false);

if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
var prevIndent = new Pos(cursor.line,
CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));

// Smart delete only if we found a valid prevIndent location
if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
}

cm.replaceRange("", deletePos, cursor, "+delete");
}
});
};

cmds.delLineRight = function(cm) {
cm.operation(function() {
var ranges = cm.listSelections();
for (var i = ranges.length - 1; i >= 0; i--)
cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
cm.scrollIntoView();
});
};

cmds.upcaseAtCursor = function(cm) {
modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
};
cmds.downcaseAtCursor = function(cm) {
modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
};

cmds.setSublimeMark = function(cm) {
if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
};
cmds.selectToSublimeMark = function(cm) {
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
if (found) cm.setSelection(cm.getCursor(), found);
};
cmds.deleteToSublimeMark = function(cm) {
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
if (found) {
var from = cm.getCursor(), to = found;
if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
cm.state.sublimeKilled = cm.getRange(from, to);
cm.replaceRange("", from, to);
}
};
cmds.swapWithSublimeMark = function(cm) {
var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
if (found) {
cm.state.sublimeMark.clear();
cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
cm.setCursor(found);
}
};
cmds.sublimeYank = function(cm) {
if (cm.state.sublimeKilled != null)
cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
};

cmds.showInCenter = function(cm) {
var pos = cm.cursorCoords(null, "local");
cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
};

function getTarget(cm) {
var from = cm.getCursor("from"), to = cm.getCursor("to");
if (CodeMirror.cmpPos(from, to) == 0) {
var word = wordAt(cm, from);
if (!word.word) return;
from = word.from;
to = word.to;
}
return {from: from, to: to, query: cm.getRange(from, to), word: word};
}

function findAndGoTo(cm, forward) {
var target = getTarget(cm);
if (!target) return;
var query = target.query;
var cur = cm.getSearchCursor(query, forward ? target.to : target.from);

if (forward ? cur.findNext() : cur.findPrevious()) {
cm.setSelection(cur.from(), cur.to());
} else {
cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
: cm.clipPos(Pos(cm.lastLine())));
if (forward ? cur.findNext() : cur.findPrevious())
cm.setSelection(cur.from(), cur.to());
else if (target.word)
cm.setSelection(target.from, target.to);
}
};
cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
cmds.findAllUnder = function(cm) {
var target = getTarget(cm);
if (!target) return;
var cur = cm.getSearchCursor(target.query);
var matches = [];
var primaryIndex = -1;
while (cur.findNext()) {
matches.push({anchor: cur.from(), head: cur.to()});
if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
primaryIndex++;
}
cm.setSelections(matches, primaryIndex);
};


var keyMap = CodeMirror.keyMap;
keyMap.macSublime = {
"Cmd-Left": "goLineStartSmart",
"Shift-Tab": "indentLess",
"Shift-Ctrl-K": "deleteLine",
"Alt-Q": "wrapLines",
"Ctrl-Left": "goSubwordLeft",
"Ctrl-Right": "goSubwordRight",
"Ctrl-Alt-Up": "scrollLineUp",
"Ctrl-Alt-Down": "scrollLineDown",
"Cmd-L": "selectLine",
"Shift-Cmd-L": "splitSelectionByLine",
"Esc": "singleSelectionTop",
"Cmd-Enter": "insertLineAfter",
"Shift-Cmd-Enter": "insertLineBefore",
"Cmd-D": "selectNextOccurrence",
"Shift-Cmd-Space": "selectScope",
"Shift-Cmd-M": "selectBetweenBrackets",
"Cmd-M": "goToBracket",
"Cmd-Ctrl-Up": "swapLineUp",
"Cmd-Ctrl-Down": "swapLineDown",
"Cmd-/": "toggleCommentIndented",
"Cmd-J": "joinLines",
"Shift-Cmd-D": "duplicateLine",
"F5": "sortLines",
"Cmd-F5": "sortLinesInsensitive",
"F2": "nextBookmark",
"Shift-F2": "prevBookmark",
"Cmd-F2": "toggleBookmark",
"Shift-Cmd-F2": "clearBookmarks",
"Alt-F2": "selectBookmarks",
"Backspace": "smartBackspace",
"Cmd-K Cmd-D": "skipAndSelectNextOccurrence",
"Cmd-K Cmd-K": "delLineRight",
"Cmd-K Cmd-U": "upcaseAtCursor",
"Cmd-K Cmd-L": "downcaseAtCursor",
"Cmd-K Cmd-Space": "setSublimeMark",
"Cmd-K Cmd-A": "selectToSublimeMark",
"Cmd-K Cmd-W": "deleteToSublimeMark",
"Cmd-K Cmd-X": "swapWithSublimeMark",
"Cmd-K Cmd-Y": "sublimeYank",
"Cmd-K Cmd-C": "showInCenter",
"Cmd-K Cmd-G": "clearBookmarks",
"Cmd-K Cmd-Backspace": "delLineLeft",
"Cmd-K Cmd-1": "foldAll",
"Cmd-K Cmd-0": "unfoldAll",
"Cmd-K Cmd-J": "unfoldAll",
"Ctrl-Shift-Up": "addCursorToPrevLine",
"Ctrl-Shift-Down": "addCursorToNextLine",
"Cmd-F3": "findUnder",
"Shift-Cmd-F3": "findUnderPrevious",
"Alt-F3": "findAllUnder",
"Shift-Cmd-[": "fold",
"Shift-Cmd-]": "unfold",
"Cmd-I": "findIncremental",
"Shift-Cmd-I": "findIncrementalReverse",
"Cmd-H": "replace",
"F3": "findNext",
"Shift-F3": "findPrev",
"fallthrough": "macDefault"
};
CodeMirror.normalizeKeyMap(keyMap.macSublime);

keyMap.pcSublime = {
"Shift-Tab": "indentLess",
"Shift-Ctrl-K": "deleteLine",
"Alt-Q": "wrapLines",
"Ctrl-T": "transposeChars",
"Alt-Left": "goSubwordLeft",
"Alt-Right": "goSubwordRight",
"Ctrl-Up": "scrollLineUp",
"Ctrl-Down": "scrollLineDown",
"Ctrl-L": "selectLine",
"Shift-Ctrl-L": "splitSelectionByLine",
"Esc": "singleSelectionTop",
"Ctrl-Enter": "insertLineAfter",
"Shift-Ctrl-Enter": "insertLineBefore",
"Ctrl-D": "selectNextOccurrence",
"Shift-Ctrl-Space": "selectScope",
"Shift-Ctrl-M": "selectBetweenBrackets",
"Ctrl-M": "goToBracket",
"Shift-Ctrl-Up": "swapLineUp",
"Shift-Ctrl-Down": "swapLineDown",
"Ctrl-/": "toggleCommentIndented",
"Ctrl-J": "joinLines",
"Shift-Ctrl-D": "duplicateLine",
"F9": "sortLines",
"Ctrl-F9": "sortLinesInsensitive",
"F2": "nextBookmark",
"Shift-F2": "prevBookmark",
"Ctrl-F2": "toggleBookmark",
"Shift-Ctrl-F2": "clearBookmarks",
"Alt-F2": "selectBookmarks",
"Backspace": "smartBackspace",
"Ctrl-K Ctrl-D": "skipAndSelectNextOccurrence",
"Ctrl-K Ctrl-K": "delLineRight",
"Ctrl-K Ctrl-U": "upcaseAtCursor",
"Ctrl-K Ctrl-L": "downcaseAtCursor",
"Ctrl-K Ctrl-Space": "setSublimeMark",
"Ctrl-K Ctrl-A": "selectToSublimeMark",
"Ctrl-K Ctrl-W": "deleteToSublimeMark",
"Ctrl-K Ctrl-X": "swapWithSublimeMark",
"Ctrl-K Ctrl-Y": "sublimeYank",
"Ctrl-K Ctrl-C": "showInCenter",
"Ctrl-K Ctrl-G": "clearBookmarks",
"Ctrl-K Ctrl-Backspace": "delLineLeft",
"Ctrl-K Ctrl-1": "foldAll",
"Ctrl-K Ctrl-0": "unfoldAll",
"Ctrl-K Ctrl-J": "unfoldAll",
"Ctrl-Alt-Up": "addCursorToPrevLine",
"Ctrl-Alt-Down": "addCursorToNextLine",
"Ctrl-F3": "findUnder",
"Shift-Ctrl-F3": "findUnderPrevious",
"Alt-F3": "findAllUnder",
"Shift-Ctrl-[": "fold",
"Shift-Ctrl-]": "unfold",
"Ctrl-I": "findIncremental",
"Shift-Ctrl-I": "findIncrementalReverse",
"Ctrl-H": "replace",
"F3": "findNext",
"Shift-F3": "findPrev",
"fallthrough": "pcDefault"
};
CodeMirror.normalizeKeyMap(keyMap.pcSublime);

var mac = keyMap.default == keyMap.macDefault;
keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
});

+ 5545
- 0
catsoop/__STATIC__/scripts/codemirror/keymap/vim.js
File diff suppressed because it is too large
View File


+ 174
- 0
catsoop/__STATIC__/scripts/codemirror/mode/apl/apl.js View File

@@ -0,0 +1,174 @@
// 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);
else // Plain browser env
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
catsoop/__STATIC__/scripts/codemirror/mode/apl/index.html View File

@@ -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>

+ 74
- 0
catsoop/__STATIC__/scripts/codemirror/mode/asciiarmor/asciiarmor.js View File

@@ -0,0 +1,74 @@
// 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);
else // Plain browser env
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 {