command_proposal 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +77 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/command_proposal_manifest.js +1 -0
  6. data/app/assets/javascripts/command_proposal/_codemirror.js +9814 -0
  7. data/app/assets/javascripts/command_proposal/_helpers.js +9 -0
  8. data/app/assets/javascripts/command_proposal/codemirror-addon-searchcursor.js +296 -0
  9. data/app/assets/javascripts/command_proposal/codemirror-keymap-sublime.js +720 -0
  10. data/app/assets/javascripts/command_proposal/codemirror-mode-ruby.js +303 -0
  11. data/app/assets/javascripts/command_proposal/console.js +195 -0
  12. data/app/assets/javascripts/command_proposal/feed.js +51 -0
  13. data/app/assets/javascripts/command_proposal/terminal.js +40 -0
  14. data/app/assets/javascripts/command_proposal.js +1 -0
  15. data/app/assets/stylesheets/command_proposal/_variables.scss +0 -0
  16. data/app/assets/stylesheets/command_proposal/codemirror-rubyblue.scss +27 -0
  17. data/app/assets/stylesheets/command_proposal/codemirror.scss +367 -0
  18. data/app/assets/stylesheets/command_proposal/command_proposal.scss +1 -0
  19. data/app/assets/stylesheets/command_proposal/components.scss +31 -0
  20. data/app/assets/stylesheets/command_proposal/containers.scss +4 -0
  21. data/app/assets/stylesheets/command_proposal/icons.scss +12 -0
  22. data/app/assets/stylesheets/command_proposal/tables.scss +76 -0
  23. data/app/assets/stylesheets/command_proposal/terminal.scss +72 -0
  24. data/app/assets/stylesheets/command_proposal.scss +5 -0
  25. data/app/controllers/command_proposal/engine_controller.rb +6 -0
  26. data/app/controllers/command_proposal/iterations_controller.rb +83 -0
  27. data/app/controllers/command_proposal/runner_controller.rb +86 -0
  28. data/app/controllers/command_proposal/tasks_controller.rb +97 -0
  29. data/app/helpers/command_proposal/application_helper.rb +58 -0
  30. data/app/helpers/command_proposal/icons_helper.rb +15 -0
  31. data/app/helpers/command_proposal/params_helper.rb +63 -0
  32. data/app/helpers/command_proposal/permissions_helper.rb +42 -0
  33. data/app/jobs/command_proposal/application_job.rb +4 -0
  34. data/app/jobs/command_proposal/command_runner_job.rb +11 -0
  35. data/app/models/command_proposal/comment.rb +14 -0
  36. data/app/models/command_proposal/iteration.rb +78 -0
  37. data/app/models/command_proposal/service/external_belong.rb +48 -0
  38. data/app/models/command_proposal/service/json_wrapper.rb +18 -0
  39. data/app/models/command_proposal/service/proposal_presenter.rb +39 -0
  40. data/app/models/command_proposal/task.rb +106 -0
  41. data/app/views/command_proposal/tasks/_console_show.html.erb +44 -0
  42. data/app/views/command_proposal/tasks/_function_show.html.erb +54 -0
  43. data/app/views/command_proposal/tasks/_lines.html.erb +8 -0
  44. data/app/views/command_proposal/tasks/_module_show.html.erb +33 -0
  45. data/app/views/command_proposal/tasks/_past_iterations_list.html.erb +20 -0
  46. data/app/views/command_proposal/tasks/_task_detail_table.html.erb +31 -0
  47. data/app/views/command_proposal/tasks/_task_show.html.erb +55 -0
  48. data/app/views/command_proposal/tasks/error.html.erb +4 -0
  49. data/app/views/command_proposal/tasks/form.html.erb +64 -0
  50. data/app/views/command_proposal/tasks/index.html.erb +44 -0
  51. data/app/views/command_proposal/tasks/show.html.erb +10 -0
  52. data/config/routes.rb +11 -0
  53. data/lib/command_proposal/configuration.rb +41 -0
  54. data/lib/command_proposal/engine.rb +6 -0
  55. data/lib/command_proposal/services/command_interpreter.rb +108 -0
  56. data/lib/command_proposal/services/runner.rb +157 -0
  57. data/lib/command_proposal/version.rb +3 -0
  58. data/lib/command_proposal.rb +27 -0
  59. data/lib/generators/command_proposal/install/install_generator.rb +28 -0
  60. data/lib/generators/command_proposal/install/templates/initializer.rb +47 -0
  61. data/lib/generators/command_proposal/install/templates/install_command_proposal.rb +40 -0
  62. data/lib/tasks/command_proposal_tasks.rake +4 -0
  63. metadata +167 -0
@@ -0,0 +1,9 @@
1
+ function docReady(fn) {
2
+ // see if DOM is already available
3
+ if (document.readyState === "complete" || document.readyState === "interactive") {
4
+ // call on next available tick
5
+ setTimeout(fn, 1)
6
+ } else {
7
+ document.addEventListener("DOMContentLoaded", fn)
8
+ }
9
+ }
@@ -0,0 +1,296 @@
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: https://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"))
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod)
9
+ else // Plain browser env
10
+ mod(CodeMirror)
11
+ })(function(CodeMirror) {
12
+ "use strict"
13
+ var Pos = CodeMirror.Pos
14
+
15
+ function regexpFlags(regexp) {
16
+ var flags = regexp.flags
17
+ return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
18
+ + (regexp.global ? "g" : "")
19
+ + (regexp.multiline ? "m" : "")
20
+ }
21
+
22
+ function ensureFlags(regexp, flags) {
23
+ var current = regexpFlags(regexp), target = current
24
+ for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
25
+ target += flags.charAt(i)
26
+ return current == target ? regexp : new RegExp(regexp.source, target)
27
+ }
28
+
29
+ function maybeMultiline(regexp) {
30
+ return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
31
+ }
32
+
33
+ function searchRegexpForward(doc, regexp, start) {
34
+ regexp = ensureFlags(regexp, "g")
35
+ for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
36
+ regexp.lastIndex = ch
37
+ var string = doc.getLine(line), match = regexp.exec(string)
38
+ if (match)
39
+ return {from: Pos(line, match.index),
40
+ to: Pos(line, match.index + match[0].length),
41
+ match: match}
42
+ }
43
+ }
44
+
45
+ function searchRegexpForwardMultiline(doc, regexp, start) {
46
+ if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
47
+
48
+ regexp = ensureFlags(regexp, "gm")
49
+ var string, chunk = 1
50
+ for (var line = start.line, last = doc.lastLine(); line <= last;) {
51
+ // This grows the search buffer in exponentially-sized chunks
52
+ // between matches, so that nearby matches are fast and don't
53
+ // require concatenating the whole document (in case we're
54
+ // searching for something that has tons of matches), but at the
55
+ // same time, the amount of retries is limited.
56
+ for (var i = 0; i < chunk; i++) {
57
+ if (line > last) break
58
+ var curLine = doc.getLine(line++)
59
+ string = string == null ? curLine : string + "\n" + curLine
60
+ }
61
+ chunk = chunk * 2
62
+ regexp.lastIndex = start.ch
63
+ var match = regexp.exec(string)
64
+ if (match) {
65
+ var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
66
+ var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
67
+ return {from: Pos(startLine, startCh),
68
+ to: Pos(startLine + inside.length - 1,
69
+ inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
70
+ match: match}
71
+ }
72
+ }
73
+ }
74
+
75
+ function lastMatchIn(string, regexp, endMargin) {
76
+ var match, from = 0
77
+ while (from <= string.length) {
78
+ regexp.lastIndex = from
79
+ var newMatch = regexp.exec(string)
80
+ if (!newMatch) break
81
+ var end = newMatch.index + newMatch[0].length
82
+ if (end > string.length - endMargin) break
83
+ if (!match || end > match.index + match[0].length)
84
+ match = newMatch
85
+ from = newMatch.index + 1
86
+ }
87
+ return match
88
+ }
89
+
90
+ function searchRegexpBackward(doc, regexp, start) {
91
+ regexp = ensureFlags(regexp, "g")
92
+ for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
93
+ var string = doc.getLine(line)
94
+ var match = lastMatchIn(string, regexp, ch < 0 ? 0 : string.length - ch)
95
+ if (match)
96
+ return {from: Pos(line, match.index),
97
+ to: Pos(line, match.index + match[0].length),
98
+ match: match}
99
+ }
100
+ }
101
+
102
+ function searchRegexpBackwardMultiline(doc, regexp, start) {
103
+ if (!maybeMultiline(regexp)) return searchRegexpBackward(doc, regexp, start)
104
+ regexp = ensureFlags(regexp, "gm")
105
+ var string, chunkSize = 1, endMargin = doc.getLine(start.line).length - start.ch
106
+ for (var line = start.line, first = doc.firstLine(); line >= first;) {
107
+ for (var i = 0; i < chunkSize && line >= first; i++) {
108
+ var curLine = doc.getLine(line--)
109
+ string = string == null ? curLine : curLine + "\n" + string
110
+ }
111
+ chunkSize *= 2
112
+
113
+ var match = lastMatchIn(string, regexp, endMargin)
114
+ if (match) {
115
+ var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
116
+ var startLine = line + before.length, startCh = before[before.length - 1].length
117
+ return {from: Pos(startLine, startCh),
118
+ to: Pos(startLine + inside.length - 1,
119
+ inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
120
+ match: match}
121
+ }
122
+ }
123
+ }
124
+
125
+ var doFold, noFold
126
+ if (String.prototype.normalize) {
127
+ doFold = function(str) { return str.normalize("NFD").toLowerCase() }
128
+ noFold = function(str) { return str.normalize("NFD") }
129
+ } else {
130
+ doFold = function(str) { return str.toLowerCase() }
131
+ noFold = function(str) { return str }
132
+ }
133
+
134
+ // Maps a position in a case-folded line back to a position in the original line
135
+ // (compensating for codepoints increasing in number during folding)
136
+ function adjustPos(orig, folded, pos, foldFunc) {
137
+ if (orig.length == folded.length) return pos
138
+ for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
139
+ if (min == max) return min
140
+ var mid = (min + max) >> 1
141
+ var len = foldFunc(orig.slice(0, mid)).length
142
+ if (len == pos) return mid
143
+ else if (len > pos) max = mid
144
+ else min = mid + 1
145
+ }
146
+ }
147
+
148
+ function searchStringForward(doc, query, start, caseFold) {
149
+ // Empty string would match anything and never progress, so we
150
+ // define it to match nothing instead.
151
+ if (!query.length) return null
152
+ var fold = caseFold ? doFold : noFold
153
+ var lines = fold(query).split(/\r|\n\r?/)
154
+
155
+ search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
156
+ var orig = doc.getLine(line).slice(ch), string = fold(orig)
157
+ if (lines.length == 1) {
158
+ var found = string.indexOf(lines[0])
159
+ if (found == -1) continue search
160
+ var start = adjustPos(orig, string, found, fold) + ch
161
+ return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
162
+ to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
163
+ } else {
164
+ var cutFrom = string.length - lines[0].length
165
+ if (string.slice(cutFrom) != lines[0]) continue search
166
+ for (var i = 1; i < lines.length - 1; i++)
167
+ if (fold(doc.getLine(line + i)) != lines[i]) continue search
168
+ var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
169
+ if (endString.slice(0, lastLine.length) != lastLine) continue search
170
+ return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
171
+ to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
172
+ }
173
+ }
174
+ }
175
+
176
+ function searchStringBackward(doc, query, start, caseFold) {
177
+ if (!query.length) return null
178
+ var fold = caseFold ? doFold : noFold
179
+ var lines = fold(query).split(/\r|\n\r?/)
180
+
181
+ search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
182
+ var orig = doc.getLine(line)
183
+ if (ch > -1) orig = orig.slice(0, ch)
184
+ var string = fold(orig)
185
+ if (lines.length == 1) {
186
+ var found = string.lastIndexOf(lines[0])
187
+ if (found == -1) continue search
188
+ return {from: Pos(line, adjustPos(orig, string, found, fold)),
189
+ to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
190
+ } else {
191
+ var lastLine = lines[lines.length - 1]
192
+ if (string.slice(0, lastLine.length) != lastLine) continue search
193
+ for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)
194
+ if (fold(doc.getLine(start + i)) != lines[i]) continue search
195
+ var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
196
+ if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
197
+ return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
198
+ to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
199
+ }
200
+ }
201
+ }
202
+
203
+ function SearchCursor(doc, query, pos, options) {
204
+ this.atOccurrence = false
205
+ this.doc = doc
206
+ pos = pos ? doc.clipPos(pos) : Pos(0, 0)
207
+ this.pos = {from: pos, to: pos}
208
+
209
+ var caseFold
210
+ if (typeof options == "object") {
211
+ caseFold = options.caseFold
212
+ } else { // Backwards compat for when caseFold was the 4th argument
213
+ caseFold = options
214
+ options = null
215
+ }
216
+
217
+ if (typeof query == "string") {
218
+ if (caseFold == null) caseFold = false
219
+ this.matches = function(reverse, pos) {
220
+ return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
221
+ }
222
+ } else {
223
+ query = ensureFlags(query, "gm")
224
+ if (!options || options.multiline !== false)
225
+ this.matches = function(reverse, pos) {
226
+ return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
227
+ }
228
+ else
229
+ this.matches = function(reverse, pos) {
230
+ return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
231
+ }
232
+ }
233
+ }
234
+
235
+ SearchCursor.prototype = {
236
+ findNext: function() {return this.find(false)},
237
+ findPrevious: function() {return this.find(true)},
238
+
239
+ find: function(reverse) {
240
+ var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))
241
+
242
+ // Implements weird auto-growing behavior on null-matches for
243
+ // backwards-compatibility with the vim code (unfortunately)
244
+ while (result && CodeMirror.cmpPos(result.from, result.to) == 0) {
245
+ if (reverse) {
246
+ if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1)
247
+ else if (result.from.line == this.doc.firstLine()) result = null
248
+ else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1)))
249
+ } else {
250
+ if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1)
251
+ else if (result.to.line == this.doc.lastLine()) result = null
252
+ else result = this.matches(reverse, Pos(result.to.line + 1, 0))
253
+ }
254
+ }
255
+
256
+ if (result) {
257
+ this.pos = result
258
+ this.atOccurrence = true
259
+ return this.pos.match || true
260
+ } else {
261
+ var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
262
+ this.pos = {from: end, to: end}
263
+ return this.atOccurrence = false
264
+ }
265
+ },
266
+
267
+ from: function() {if (this.atOccurrence) return this.pos.from},
268
+ to: function() {if (this.atOccurrence) return this.pos.to},
269
+
270
+ replace: function(newText, origin) {
271
+ if (!this.atOccurrence) return
272
+ var lines = CodeMirror.splitLines(newText)
273
+ this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
274
+ this.pos.to = Pos(this.pos.from.line + lines.length - 1,
275
+ lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
276
+ }
277
+ }
278
+
279
+ CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
280
+ return new SearchCursor(this.doc, query, pos, caseFold)
281
+ })
282
+ CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
283
+ return new SearchCursor(this, query, pos, caseFold)
284
+ })
285
+
286
+ CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
287
+ var ranges = []
288
+ var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
289
+ while (cur.findNext()) {
290
+ if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
291
+ ranges.push({anchor: cur.from(), head: cur.to()})
292
+ }
293
+ if (ranges.length)
294
+ this.setSelections(ranges, 0)
295
+ })
296
+ });
@@ -0,0 +1,720 @@
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: https://codemirror.net/LICENSE
3
+
4
+ // A rough approximation of Sublime Text's keybindings
5
+ // Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
6
+
7
+ (function(mod) {
8
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
9
+ mod(require("../lib/codemirror"), require("../addon/search/searchcursor"), require("../addon/edit/matchbrackets"));
10
+ else if (typeof define == "function" && define.amd) // AMD
11
+ define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
12
+ else // Plain browser env
13
+ mod(CodeMirror);
14
+ })(function(CodeMirror) {
15
+ "use strict";
16
+
17
+ var cmds = CodeMirror.commands;
18
+ var Pos = CodeMirror.Pos;
19
+
20
+ // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
21
+ function findPosSubword(doc, start, dir) {
22
+ if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
23
+ var line = doc.getLine(start.line);
24
+ if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
25
+ var state = "start", type, startPos = start.ch;
26
+ for (var pos = startPos, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
27
+ var next = line.charAt(dir < 0 ? pos - 1 : pos);
28
+ var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
29
+ if (cat == "w" && next.toUpperCase() == next) cat = "W";
30
+ if (state == "start") {
31
+ if (cat != "o") { state = "in"; type = cat; }
32
+ else startPos = pos + dir
33
+ } else if (state == "in") {
34
+ if (type != cat) {
35
+ if (type == "w" && cat == "W" && dir < 0) pos--;
36
+ if (type == "W" && cat == "w" && dir > 0) { // From uppercase to lowercase
37
+ if (pos == startPos + 1) { type = "w"; continue; }
38
+ else pos--;
39
+ }
40
+ break;
41
+ }
42
+ }
43
+ }
44
+ return Pos(start.line, pos);
45
+ }
46
+
47
+ function moveSubword(cm, dir) {
48
+ cm.extendSelectionsBy(function(range) {
49
+ if (cm.display.shift || cm.doc.extend || range.empty())
50
+ return findPosSubword(cm.doc, range.head, dir);
51
+ else
52
+ return dir < 0 ? range.from() : range.to();
53
+ });
54
+ }
55
+
56
+ cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
57
+ cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
58
+
59
+ cmds.scrollLineUp = function(cm) {
60
+ var info = cm.getScrollInfo();
61
+ if (!cm.somethingSelected()) {
62
+ var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
63
+ if (cm.getCursor().line >= visibleBottomLine)
64
+ cm.execCommand("goLineUp");
65
+ }
66
+ cm.scrollTo(null, info.top - cm.defaultTextHeight());
67
+ };
68
+ cmds.scrollLineDown = function(cm) {
69
+ var info = cm.getScrollInfo();
70
+ if (!cm.somethingSelected()) {
71
+ var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
72
+ if (cm.getCursor().line <= visibleTopLine)
73
+ cm.execCommand("goLineDown");
74
+ }
75
+ cm.scrollTo(null, info.top + cm.defaultTextHeight());
76
+ };
77
+
78
+ cmds.splitSelectionByLine = function(cm) {
79
+ var ranges = cm.listSelections(), lineRanges = [];
80
+ for (var i = 0; i < ranges.length; i++) {
81
+ var from = ranges[i].from(), to = ranges[i].to();
82
+ for (var line = from.line; line <= to.line; ++line)
83
+ if (!(to.line > from.line && line == to.line && to.ch == 0))
84
+ lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
85
+ head: line == to.line ? to : Pos(line)});
86
+ }
87
+ cm.setSelections(lineRanges, 0);
88
+ };
89
+
90
+ cmds.singleSelectionTop = function(cm) {
91
+ var range = cm.listSelections()[0];
92
+ cm.setSelection(range.anchor, range.head, {scroll: false});
93
+ };
94
+
95
+ cmds.selectLine = function(cm) {
96
+ var ranges = cm.listSelections(), extended = [];
97
+ for (var i = 0; i < ranges.length; i++) {
98
+ var range = ranges[i];
99
+ extended.push({anchor: Pos(range.from().line, 0),
100
+ head: Pos(range.to().line + 1, 0)});
101
+ }
102
+ cm.setSelections(extended);
103
+ };
104
+
105
+ function insertLine(cm, above) {
106
+ if (cm.isReadOnly()) return CodeMirror.Pass
107
+ cm.operation(function() {
108
+ var len = cm.listSelections().length, newSelection = [], last = -1;
109
+ for (var i = 0; i < len; i++) {
110
+ var head = cm.listSelections()[i].head;
111
+ if (head.line <= last) continue;
112
+ var at = Pos(head.line + (above ? 0 : 1), 0);
113
+ cm.replaceRange("\n", at, null, "+insertLine");
114
+ cm.indentLine(at.line, null, true);
115
+ newSelection.push({head: at, anchor: at});
116
+ last = head.line + 1;
117
+ }
118
+ cm.setSelections(newSelection);
119
+ });
120
+ cm.execCommand("indentAuto");
121
+ }
122
+
123
+ cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
124
+
125
+ cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
126
+
127
+ function wordAt(cm, pos) {
128
+ var start = pos.ch, end = start, line = cm.getLine(pos.line);
129
+ while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
130
+ while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
131
+ return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
132
+ }
133
+
134
+ cmds.selectNextOccurrence = function(cm) {
135
+ var from = cm.getCursor("from"), to = cm.getCursor("to");
136
+ var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
137
+ if (CodeMirror.cmpPos(from, to) == 0) {
138
+ var word = wordAt(cm, from);
139
+ if (!word.word) return;
140
+ cm.setSelection(word.from, word.to);
141
+ fullWord = true;
142
+ } else {
143
+ var text = cm.getRange(from, to);
144
+ var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
145
+ var cur = cm.getSearchCursor(query, to);
146
+ var found = cur.findNext();
147
+ if (!found) {
148
+ cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
149
+ found = cur.findNext();
150
+ }
151
+ if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to())) return
152
+ cm.addSelection(cur.from(), cur.to());
153
+ }
154
+ if (fullWord)
155
+ cm.state.sublimeFindFullWord = cm.doc.sel;
156
+ };
157
+
158
+ cmds.skipAndSelectNextOccurrence = function(cm) {
159
+ var prevAnchor = cm.getCursor("anchor"), prevHead = cm.getCursor("head");
160
+ cmds.selectNextOccurrence(cm);
161
+ if (CodeMirror.cmpPos(prevAnchor, prevHead) != 0) {
162
+ cm.doc.setSelections(cm.doc.listSelections()
163
+ .filter(function (sel) {
164
+ return sel.anchor != prevAnchor || sel.head != prevHead;
165
+ }));
166
+ }
167
+ }
168
+
169
+ function addCursorToSelection(cm, dir) {
170
+ var ranges = cm.listSelections(), newRanges = [];
171
+ for (var i = 0; i < ranges.length; i++) {
172
+ var range = ranges[i];
173
+ var newAnchor = cm.findPosV(
174
+ range.anchor, dir, "line", range.anchor.goalColumn);
175
+ var newHead = cm.findPosV(
176
+ range.head, dir, "line", range.head.goalColumn);
177
+ newAnchor.goalColumn = range.anchor.goalColumn != null ?
178
+ range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
179
+ newHead.goalColumn = range.head.goalColumn != null ?
180
+ range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
181
+ var newRange = {anchor: newAnchor, head: newHead};
182
+ newRanges.push(range);
183
+ newRanges.push(newRange);
184
+ }
185
+ cm.setSelections(newRanges);
186
+ }
187
+ cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
188
+ cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
189
+
190
+ function isSelectedRange(ranges, from, to) {
191
+ for (var i = 0; i < ranges.length; i++)
192
+ if (CodeMirror.cmpPos(ranges[i].from(), from) == 0 &&
193
+ CodeMirror.cmpPos(ranges[i].to(), to) == 0) return true
194
+ return false
195
+ }
196
+
197
+ var mirror = "(){}[]";
198
+ function selectBetweenBrackets(cm) {
199
+ var ranges = cm.listSelections(), newRanges = []
200
+ for (var i = 0; i < ranges.length; i++) {
201
+ var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
202
+ if (!opening) return false;
203
+ for (;;) {
204
+ var closing = cm.scanForBracket(pos, 1);
205
+ if (!closing) return false;
206
+ if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
207
+ var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
208
+ if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
209
+ CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
210
+ opening = cm.scanForBracket(opening.pos, -1);
211
+ if (!opening) return false;
212
+ } else {
213
+ newRanges.push({anchor: startPos, head: closing.pos});
214
+ break;
215
+ }
216
+ }
217
+ pos = Pos(closing.pos.line, closing.pos.ch + 1);
218
+ }
219
+ }
220
+ cm.setSelections(newRanges);
221
+ return true;
222
+ }
223
+
224
+ cmds.selectScope = function(cm) {
225
+ selectBetweenBrackets(cm) || cm.execCommand("selectAll");
226
+ };
227
+ cmds.selectBetweenBrackets = function(cm) {
228
+ if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
229
+ };
230
+
231
+ function puncType(type) {
232
+ return !type ? null : /\bpunctuation\b/.test(type) ? type : undefined
233
+ }
234
+
235
+ cmds.goToBracket = function(cm) {
236
+ cm.extendSelectionsBy(function(range) {
237
+ var next = cm.scanForBracket(range.head, 1, puncType(cm.getTokenTypeAt(range.head)));
238
+ if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
239
+ var prev = cm.scanForBracket(range.head, -1, puncType(cm.getTokenTypeAt(Pos(range.head.line, range.head.ch + 1))));
240
+ return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
241
+ });
242
+ };
243
+
244
+ cmds.swapLineUp = function(cm) {
245
+ if (cm.isReadOnly()) return CodeMirror.Pass
246
+ var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
247
+ for (var i = 0; i < ranges.length; i++) {
248
+ var range = ranges[i], from = range.from().line - 1, to = range.to().line;
249
+ newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
250
+ head: Pos(range.head.line - 1, range.head.ch)});
251
+ if (range.to().ch == 0 && !range.empty()) --to;
252
+ if (from > at) linesToMove.push(from, to);
253
+ else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
254
+ at = to;
255
+ }
256
+ cm.operation(function() {
257
+ for (var i = 0; i < linesToMove.length; i += 2) {
258
+ var from = linesToMove[i], to = linesToMove[i + 1];
259
+ var line = cm.getLine(from);
260
+ cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
261
+ if (to > cm.lastLine())
262
+ cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
263
+ else
264
+ cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
265
+ }
266
+ cm.setSelections(newSels);
267
+ cm.scrollIntoView();
268
+ });
269
+ };
270
+
271
+ cmds.swapLineDown = function(cm) {
272
+ if (cm.isReadOnly()) return CodeMirror.Pass
273
+ var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
274
+ for (var i = ranges.length - 1; i >= 0; i--) {
275
+ var range = ranges[i], from = range.to().line + 1, to = range.from().line;
276
+ if (range.to().ch == 0 && !range.empty()) from--;
277
+ if (from < at) linesToMove.push(from, to);
278
+ else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
279
+ at = to;
280
+ }
281
+ cm.operation(function() {
282
+ for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
283
+ var from = linesToMove[i], to = linesToMove[i + 1];
284
+ var line = cm.getLine(from);
285
+ if (from == cm.lastLine())
286
+ cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
287
+ else
288
+ cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
289
+ cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
290
+ }
291
+ cm.scrollIntoView();
292
+ });
293
+ };
294
+
295
+ cmds.toggleCommentIndented = function(cm) {
296
+ cm.toggleComment({ indent: true });
297
+ }
298
+
299
+ cmds.joinLines = function(cm) {
300
+ var ranges = cm.listSelections(), joined = [];
301
+ for (var i = 0; i < ranges.length; i++) {
302
+ var range = ranges[i], from = range.from();
303
+ var start = from.line, end = range.to().line;
304
+ while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
305
+ end = ranges[++i].to().line;
306
+ joined.push({start: start, end: end, anchor: !range.empty() && from});
307
+ }
308
+ cm.operation(function() {
309
+ var offset = 0, ranges = [];
310
+ for (var i = 0; i < joined.length; i++) {
311
+ var obj = joined[i];
312
+ var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
313
+ for (var line = obj.start; line <= obj.end; line++) {
314
+ var actual = line - offset;
315
+ if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
316
+ if (actual < cm.lastLine()) {
317
+ cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
318
+ ++offset;
319
+ }
320
+ }
321
+ ranges.push({anchor: anchor || head, head: head});
322
+ }
323
+ cm.setSelections(ranges, 0);
324
+ });
325
+ };
326
+
327
+ cmds.duplicateLine = function(cm) {
328
+ cm.operation(function() {
329
+ var rangeCount = cm.listSelections().length;
330
+ for (var i = 0; i < rangeCount; i++) {
331
+ var range = cm.listSelections()[i];
332
+ if (range.empty())
333
+ cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
334
+ else
335
+ cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
336
+ }
337
+ cm.scrollIntoView();
338
+ });
339
+ };
340
+
341
+
342
+ function sortLines(cm, caseSensitive, direction) {
343
+ if (cm.isReadOnly()) return CodeMirror.Pass
344
+ var ranges = cm.listSelections(), toSort = [], selected;
345
+ for (var i = 0; i < ranges.length; i++) {
346
+ var range = ranges[i];
347
+ if (range.empty()) continue;
348
+ var from = range.from().line, to = range.to().line;
349
+ while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
350
+ to = ranges[++i].to().line;
351
+ if (!ranges[i].to().ch) to--;
352
+ toSort.push(from, to);
353
+ }
354
+ if (toSort.length) selected = true;
355
+ else toSort.push(cm.firstLine(), cm.lastLine());
356
+
357
+ cm.operation(function() {
358
+ var ranges = [];
359
+ for (var i = 0; i < toSort.length; i += 2) {
360
+ var from = toSort[i], to = toSort[i + 1];
361
+ var start = Pos(from, 0), end = Pos(to);
362
+ var lines = cm.getRange(start, end, false);
363
+ if (caseSensitive)
364
+ lines.sort(function(a, b) { return a < b ? -direction : a == b ? 0 : direction; });
365
+ else
366
+ lines.sort(function(a, b) {
367
+ var au = a.toUpperCase(), bu = b.toUpperCase();
368
+ if (au != bu) { a = au; b = bu; }
369
+ return a < b ? -direction : a == b ? 0 : direction;
370
+ });
371
+ cm.replaceRange(lines, start, end);
372
+ if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});
373
+ }
374
+ if (selected) cm.setSelections(ranges, 0);
375
+ });
376
+ }
377
+
378
+ cmds.sortLines = function(cm) { sortLines(cm, true, 1); };
379
+ cmds.reverseSortLines = function(cm) { sortLines(cm, true, -1); };
380
+ cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false, 1); };
381
+ cmds.reverseSortLinesInsensitive = function(cm) { sortLines(cm, false, -1); };
382
+
383
+ cmds.nextBookmark = function(cm) {
384
+ var marks = cm.state.sublimeBookmarks;
385
+ if (marks) while (marks.length) {
386
+ var current = marks.shift();
387
+ var found = current.find();
388
+ if (found) {
389
+ marks.push(current);
390
+ return cm.setSelection(found.from, found.to);
391
+ }
392
+ }
393
+ };
394
+
395
+ cmds.prevBookmark = function(cm) {
396
+ var marks = cm.state.sublimeBookmarks;
397
+ if (marks) while (marks.length) {
398
+ marks.unshift(marks.pop());
399
+ var found = marks[marks.length - 1].find();
400
+ if (!found)
401
+ marks.pop();
402
+ else
403
+ return cm.setSelection(found.from, found.to);
404
+ }
405
+ };
406
+
407
+ cmds.toggleBookmark = function(cm) {
408
+ var ranges = cm.listSelections();
409
+ var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
410
+ for (var i = 0; i < ranges.length; i++) {
411
+ var from = ranges[i].from(), to = ranges[i].to();
412
+ var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
413
+ for (var j = 0; j < found.length; j++) {
414
+ if (found[j].sublimeBookmark) {
415
+ found[j].clear();
416
+ for (var k = 0; k < marks.length; k++)
417
+ if (marks[k] == found[j])
418
+ marks.splice(k--, 1);
419
+ break;
420
+ }
421
+ }
422
+ if (j == found.length)
423
+ marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
424
+ }
425
+ };
426
+
427
+ cmds.clearBookmarks = function(cm) {
428
+ var marks = cm.state.sublimeBookmarks;
429
+ if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
430
+ marks.length = 0;
431
+ };
432
+
433
+ cmds.selectBookmarks = function(cm) {
434
+ var marks = cm.state.sublimeBookmarks, ranges = [];
435
+ if (marks) for (var i = 0; i < marks.length; i++) {
436
+ var found = marks[i].find();
437
+ if (!found)
438
+ marks.splice(i--, 0);
439
+ else
440
+ ranges.push({anchor: found.from, head: found.to});
441
+ }
442
+ if (ranges.length)
443
+ cm.setSelections(ranges, 0);
444
+ };
445
+
446
+ function modifyWordOrSelection(cm, mod) {
447
+ cm.operation(function() {
448
+ var ranges = cm.listSelections(), indices = [], replacements = [];
449
+ for (var i = 0; i < ranges.length; i++) {
450
+ var range = ranges[i];
451
+ if (range.empty()) { indices.push(i); replacements.push(""); }
452
+ else replacements.push(mod(cm.getRange(range.from(), range.to())));
453
+ }
454
+ cm.replaceSelections(replacements, "around", "case");
455
+ for (var i = indices.length - 1, at; i >= 0; i--) {
456
+ var range = ranges[indices[i]];
457
+ if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
458
+ var word = wordAt(cm, range.head);
459
+ at = word.from;
460
+ cm.replaceRange(mod(word.word), word.from, word.to);
461
+ }
462
+ });
463
+ }
464
+
465
+ cmds.smartBackspace = function(cm) {
466
+ if (cm.somethingSelected()) return CodeMirror.Pass;
467
+
468
+ cm.operation(function() {
469
+ var cursors = cm.listSelections();
470
+ var indentUnit = cm.getOption("indentUnit");
471
+
472
+ for (var i = cursors.length - 1; i >= 0; i--) {
473
+ var cursor = cursors[i].head;
474
+ var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
475
+ var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
476
+
477
+ // Delete by one character by default
478
+ var deletePos = cm.findPosH(cursor, -1, "char", false);
479
+
480
+ if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
481
+ var prevIndent = new Pos(cursor.line,
482
+ CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
483
+
484
+ // Smart delete only if we found a valid prevIndent location
485
+ if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
486
+ }
487
+
488
+ cm.replaceRange("", deletePos, cursor, "+delete");
489
+ }
490
+ });
491
+ };
492
+
493
+ cmds.delLineRight = function(cm) {
494
+ cm.operation(function() {
495
+ var ranges = cm.listSelections();
496
+ for (var i = ranges.length - 1; i >= 0; i--)
497
+ cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
498
+ cm.scrollIntoView();
499
+ });
500
+ };
501
+
502
+ cmds.upcaseAtCursor = function(cm) {
503
+ modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
504
+ };
505
+ cmds.downcaseAtCursor = function(cm) {
506
+ modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
507
+ };
508
+
509
+ cmds.setSublimeMark = function(cm) {
510
+ if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
511
+ cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
512
+ };
513
+ cmds.selectToSublimeMark = function(cm) {
514
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
515
+ if (found) cm.setSelection(cm.getCursor(), found);
516
+ };
517
+ cmds.deleteToSublimeMark = function(cm) {
518
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
519
+ if (found) {
520
+ var from = cm.getCursor(), to = found;
521
+ if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
522
+ cm.state.sublimeKilled = cm.getRange(from, to);
523
+ cm.replaceRange("", from, to);
524
+ }
525
+ };
526
+ cmds.swapWithSublimeMark = function(cm) {
527
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
528
+ if (found) {
529
+ cm.state.sublimeMark.clear();
530
+ cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
531
+ cm.setCursor(found);
532
+ }
533
+ };
534
+ cmds.sublimeYank = function(cm) {
535
+ if (cm.state.sublimeKilled != null)
536
+ cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
537
+ };
538
+
539
+ cmds.showInCenter = function(cm) {
540
+ var pos = cm.cursorCoords(null, "local");
541
+ cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
542
+ };
543
+
544
+ function getTarget(cm) {
545
+ var from = cm.getCursor("from"), to = cm.getCursor("to");
546
+ if (CodeMirror.cmpPos(from, to) == 0) {
547
+ var word = wordAt(cm, from);
548
+ if (!word.word) return;
549
+ from = word.from;
550
+ to = word.to;
551
+ }
552
+ return {from: from, to: to, query: cm.getRange(from, to), word: word};
553
+ }
554
+
555
+ function findAndGoTo(cm, forward) {
556
+ var target = getTarget(cm);
557
+ if (!target) return;
558
+ var query = target.query;
559
+ var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
560
+
561
+ if (forward ? cur.findNext() : cur.findPrevious()) {
562
+ cm.setSelection(cur.from(), cur.to());
563
+ } else {
564
+ cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
565
+ : cm.clipPos(Pos(cm.lastLine())));
566
+ if (forward ? cur.findNext() : cur.findPrevious())
567
+ cm.setSelection(cur.from(), cur.to());
568
+ else if (target.word)
569
+ cm.setSelection(target.from, target.to);
570
+ }
571
+ };
572
+ cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
573
+ cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
574
+ cmds.findAllUnder = function(cm) {
575
+ var target = getTarget(cm);
576
+ if (!target) return;
577
+ var cur = cm.getSearchCursor(target.query);
578
+ var matches = [];
579
+ var primaryIndex = -1;
580
+ while (cur.findNext()) {
581
+ matches.push({anchor: cur.from(), head: cur.to()});
582
+ if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
583
+ primaryIndex++;
584
+ }
585
+ cm.setSelections(matches, primaryIndex);
586
+ };
587
+
588
+
589
+ var keyMap = CodeMirror.keyMap;
590
+ keyMap.macSublime = {
591
+ "Cmd-Left": "goLineStartSmart",
592
+ "Shift-Tab": "indentLess",
593
+ "Shift-Ctrl-K": "deleteLine",
594
+ "Alt-Q": "wrapLines",
595
+ "Ctrl-Left": "goSubwordLeft",
596
+ "Ctrl-Right": "goSubwordRight",
597
+ "Ctrl-Alt-Up": "scrollLineUp",
598
+ "Ctrl-Alt-Down": "scrollLineDown",
599
+ "Cmd-L": "selectLine",
600
+ "Shift-Cmd-L": "splitSelectionByLine",
601
+ "Esc": "singleSelectionTop",
602
+ "Cmd-Enter": "insertLineAfter",
603
+ "Shift-Cmd-Enter": "insertLineBefore",
604
+ "Cmd-D": "selectNextOccurrence",
605
+ "Shift-Cmd-Space": "selectScope",
606
+ "Shift-Cmd-M": "selectBetweenBrackets",
607
+ "Cmd-M": "goToBracket",
608
+ "Cmd-Ctrl-Up": "swapLineUp",
609
+ "Cmd-Ctrl-Down": "swapLineDown",
610
+ "Cmd-/": "toggleCommentIndented",
611
+ "Cmd-J": "joinLines",
612
+ "Shift-Cmd-D": "duplicateLine",
613
+ "F5": "sortLines",
614
+ "Shift-F5": "reverseSortLines",
615
+ "Cmd-F5": "sortLinesInsensitive",
616
+ "Shift-Cmd-F5": "reverseSortLinesInsensitive",
617
+ "F2": "nextBookmark",
618
+ "Shift-F2": "prevBookmark",
619
+ "Cmd-F2": "toggleBookmark",
620
+ "Shift-Cmd-F2": "clearBookmarks",
621
+ "Alt-F2": "selectBookmarks",
622
+ "Backspace": "smartBackspace",
623
+ "Cmd-K Cmd-D": "skipAndSelectNextOccurrence",
624
+ "Cmd-K Cmd-K": "delLineRight",
625
+ "Cmd-K Cmd-U": "upcaseAtCursor",
626
+ "Cmd-K Cmd-L": "downcaseAtCursor",
627
+ "Cmd-K Cmd-Space": "setSublimeMark",
628
+ "Cmd-K Cmd-A": "selectToSublimeMark",
629
+ "Cmd-K Cmd-W": "deleteToSublimeMark",
630
+ "Cmd-K Cmd-X": "swapWithSublimeMark",
631
+ "Cmd-K Cmd-Y": "sublimeYank",
632
+ "Cmd-K Cmd-C": "showInCenter",
633
+ "Cmd-K Cmd-G": "clearBookmarks",
634
+ "Cmd-K Cmd-Backspace": "delLineLeft",
635
+ "Cmd-K Cmd-1": "foldAll",
636
+ "Cmd-K Cmd-0": "unfoldAll",
637
+ "Cmd-K Cmd-J": "unfoldAll",
638
+ "Ctrl-Shift-Up": "addCursorToPrevLine",
639
+ "Ctrl-Shift-Down": "addCursorToNextLine",
640
+ "Cmd-F3": "findUnder",
641
+ "Shift-Cmd-F3": "findUnderPrevious",
642
+ "Alt-F3": "findAllUnder",
643
+ "Shift-Cmd-[": "fold",
644
+ "Shift-Cmd-]": "unfold",
645
+ "Cmd-I": "findIncremental",
646
+ "Shift-Cmd-I": "findIncrementalReverse",
647
+ "Cmd-H": "replace",
648
+ "F3": "findNext",
649
+ "Shift-F3": "findPrev",
650
+ "fallthrough": "macDefault"
651
+ };
652
+ CodeMirror.normalizeKeyMap(keyMap.macSublime);
653
+
654
+ keyMap.pcSublime = {
655
+ "Shift-Tab": "indentLess",
656
+ "Shift-Ctrl-K": "deleteLine",
657
+ "Alt-Q": "wrapLines",
658
+ "Ctrl-T": "transposeChars",
659
+ "Alt-Left": "goSubwordLeft",
660
+ "Alt-Right": "goSubwordRight",
661
+ "Ctrl-Up": "scrollLineUp",
662
+ "Ctrl-Down": "scrollLineDown",
663
+ "Ctrl-L": "selectLine",
664
+ "Shift-Ctrl-L": "splitSelectionByLine",
665
+ "Esc": "singleSelectionTop",
666
+ "Ctrl-Enter": "insertLineAfter",
667
+ "Shift-Ctrl-Enter": "insertLineBefore",
668
+ "Ctrl-D": "selectNextOccurrence",
669
+ "Shift-Ctrl-Space": "selectScope",
670
+ "Shift-Ctrl-M": "selectBetweenBrackets",
671
+ "Ctrl-M": "goToBracket",
672
+ "Shift-Ctrl-Up": "swapLineUp",
673
+ "Shift-Ctrl-Down": "swapLineDown",
674
+ "Ctrl-/": "toggleCommentIndented",
675
+ "Ctrl-J": "joinLines",
676
+ "Shift-Ctrl-D": "duplicateLine",
677
+ "F9": "sortLines",
678
+ "Shift-F9": "reverseSortLines",
679
+ "Ctrl-F9": "sortLinesInsensitive",
680
+ "Shift-Ctrl-F9": "reverseSortLinesInsensitive",
681
+ "F2": "nextBookmark",
682
+ "Shift-F2": "prevBookmark",
683
+ "Ctrl-F2": "toggleBookmark",
684
+ "Shift-Ctrl-F2": "clearBookmarks",
685
+ "Alt-F2": "selectBookmarks",
686
+ "Backspace": "smartBackspace",
687
+ "Ctrl-K Ctrl-D": "skipAndSelectNextOccurrence",
688
+ "Ctrl-K Ctrl-K": "delLineRight",
689
+ "Ctrl-K Ctrl-U": "upcaseAtCursor",
690
+ "Ctrl-K Ctrl-L": "downcaseAtCursor",
691
+ "Ctrl-K Ctrl-Space": "setSublimeMark",
692
+ "Ctrl-K Ctrl-A": "selectToSublimeMark",
693
+ "Ctrl-K Ctrl-W": "deleteToSublimeMark",
694
+ "Ctrl-K Ctrl-X": "swapWithSublimeMark",
695
+ "Ctrl-K Ctrl-Y": "sublimeYank",
696
+ "Ctrl-K Ctrl-C": "showInCenter",
697
+ "Ctrl-K Ctrl-G": "clearBookmarks",
698
+ "Ctrl-K Ctrl-Backspace": "delLineLeft",
699
+ "Ctrl-K Ctrl-1": "foldAll",
700
+ "Ctrl-K Ctrl-0": "unfoldAll",
701
+ "Ctrl-K Ctrl-J": "unfoldAll",
702
+ "Ctrl-Alt-Up": "addCursorToPrevLine",
703
+ "Ctrl-Alt-Down": "addCursorToNextLine",
704
+ "Ctrl-F3": "findUnder",
705
+ "Shift-Ctrl-F3": "findUnderPrevious",
706
+ "Alt-F3": "findAllUnder",
707
+ "Shift-Ctrl-[": "fold",
708
+ "Shift-Ctrl-]": "unfold",
709
+ "Ctrl-I": "findIncremental",
710
+ "Shift-Ctrl-I": "findIncrementalReverse",
711
+ "Ctrl-H": "replace",
712
+ "F3": "findNext",
713
+ "Shift-F3": "findPrev",
714
+ "fallthrough": "pcDefault"
715
+ };
716
+ CodeMirror.normalizeKeyMap(keyMap.pcSublime);
717
+
718
+ var mac = keyMap.default == keyMap.macDefault;
719
+ keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
720
+ });