codemirror-rails 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +40 -7
- data/lib/codemirror/rails.rb +2 -0
- data/lib/codemirror/rails/engine.rb +6 -0
- data/lib/codemirror/rails/version.rb +2 -2
- data/vendor/assets/javascripts/codemirror.js +85 -45
- data/vendor/assets/javascripts/codemirror/modes/clojure.js +207 -0
- data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +42 -25
- data/vendor/assets/javascripts/codemirror/modes/css.js +3 -3
- data/vendor/assets/javascripts/codemirror/modes/javascript.js +4 -0
- data/vendor/assets/javascripts/codemirror/modes/jinja2.js +42 -0
- data/vendor/assets/javascripts/codemirror/modes/lua.js +4 -2
- data/vendor/assets/javascripts/codemirror/modes/markdown.js +230 -0
- data/vendor/assets/javascripts/codemirror/modes/ntriples.js +172 -0
- data/vendor/assets/javascripts/codemirror/modes/pascal.js +138 -0
- data/vendor/assets/javascripts/codemirror/modes/php.js +3 -2
- data/vendor/assets/javascripts/codemirror/modes/python.js +5 -6
- data/vendor/assets/javascripts/codemirror/modes/r.js +9 -2
- data/vendor/assets/javascripts/codemirror/modes/ruby.js +36 -23
- data/vendor/assets/javascripts/codemirror/modes/scheme.js +51 -31
- data/vendor/assets/javascripts/codemirror/modes/xml.js +4 -4
- data/vendor/assets/stylesheets/codemirror.css +1 -0
- data/vendor/assets/stylesheets/codemirror/modes/markdown.css +10 -0
- data/vendor/assets/stylesheets/codemirror/themes/cobalt.css +17 -0
- data/vendor/assets/stylesheets/codemirror/themes/eclipse.css +24 -0
- metadata +12 -5
data/README.md
CHANGED
@@ -1,9 +1,42 @@
|
|
1
|
-
|
2
|
-
======================
|
1
|
+
# codemirror-rails
|
3
2
|
|
4
|
-
|
5
|
-
|
3
|
+
Wire up the [CodeMirror](http://codemirror.net/) assets for your Rails
|
4
|
+
applications.
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
## Getting Started
|
7
|
+
|
8
|
+
If you're using Bundler, you can add codemirror-rails to your Gemfile:
|
9
|
+
|
10
|
+
gem 'codemirror-rails'
|
11
|
+
|
12
|
+
Or manually install the codemirror-rails gem:
|
13
|
+
|
14
|
+
gem install codemirror-rails
|
15
|
+
|
16
|
+
|
17
|
+
## CodeMirror for Rails 3.1
|
18
|
+
|
19
|
+
All of the assets from the most latest stable CodeMirror release are vendored
|
20
|
+
so that you can use them with the asset pipeline. At a minimum, you will
|
21
|
+
probably want the following in your application.js and application.css:
|
22
|
+
|
23
|
+
//= require codemirror
|
24
|
+
|
25
|
+
### Adding a mode
|
26
|
+
|
27
|
+
Additional syntax modes can be added to your application.js:
|
28
|
+
|
29
|
+
//= require codemirror/modes/ruby
|
30
|
+
|
31
|
+
### Adding a theme
|
32
|
+
|
33
|
+
Additional CSS themes can be added to your application.css
|
34
|
+
|
35
|
+
//= require codemirror/themes/night
|
36
|
+
|
37
|
+
## CodeMirror for Rails 3
|
38
|
+
|
39
|
+
You can use the generator included with this gem to copy the CodeMirror 2
|
40
|
+
assets into your Rails 3 public directory.
|
41
|
+
|
42
|
+
rails generate codemirror:install
|
data/lib/codemirror/rails.rb
CHANGED
@@ -20,14 +20,15 @@ var CodeMirror = (function() {
|
|
20
20
|
// This mess creates the base DOM structure for the editor.
|
21
21
|
wrapper.innerHTML =
|
22
22
|
'<div style="overflow: hidden; position: relative; width: 1px; height: 0px;">' + // Wraps and hides input textarea
|
23
|
-
'<textarea style="position: absolute; width: 2px;" wrap="off"
|
23
|
+
'<textarea style="position: absolute; width: 2px;" wrap="off" ' +
|
24
|
+
'autocorrect="off" autocapitalize="off"></textarea></div>' +
|
24
25
|
'<div class="CodeMirror-scroll cm-s-' + options.theme + '">' +
|
25
26
|
'<div style="position: relative">' + // Set to the height of the text, causes scrolling
|
26
27
|
'<div style="position: absolute; height: 0; width: 0; overflow: hidden;"></div>' +
|
27
28
|
'<div style="position: relative">' + // Moved around its parent to cover visible view
|
28
29
|
'<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
|
29
30
|
// Provides positioning relative to (visible) text origin
|
30
|
-
'<div class="CodeMirror-lines"><div style="position: relative">' +
|
31
|
+
'<div class="CodeMirror-lines"><div style="position: relative" draggable="true">' +
|
31
32
|
'<pre class="CodeMirror-cursor"> </pre>' + // Absolutely positioned blinky cursor
|
32
33
|
'<div></div>' + // This DIV contains the actual code
|
33
34
|
'</div></div></div></div></div>';
|
@@ -59,10 +60,10 @@ var CodeMirror = (function() {
|
|
59
60
|
// whether the user is holding shift. reducedSelection is a hack
|
60
61
|
// to get around the fact that we can't create inverted
|
61
62
|
// selections. See below.
|
62
|
-
var shiftSelecting, reducedSelection, lastDoubleClick;
|
63
|
+
var shiftSelecting, reducedSelection, lastClick, lastDoubleClick;
|
63
64
|
// Variables used by startOperation/endOperation to track what
|
64
65
|
// happened during the operation.
|
65
|
-
var updateInput, changes, textChanged, selectionChanged, leaveInputAlone;
|
66
|
+
var updateInput, changes, textChanged, selectionChanged, leaveInputAlone, gutterDirty;
|
66
67
|
// Current visible range (may be bigger than the view window).
|
67
68
|
var showingFrom = 0, showingTo = 0, lastHeight = 0, curKeyId = null;
|
68
69
|
// editing will hold an object describing the things we put in the
|
@@ -79,12 +80,16 @@ var CodeMirror = (function() {
|
|
79
80
|
|
80
81
|
// Register our event handlers.
|
81
82
|
connect(scroller, "mousedown", operation(onMouseDown));
|
83
|
+
connect(lineSpace, "dragstart", onDragStart);
|
82
84
|
// Gecko browsers fire contextmenu *after* opening the menu, at
|
83
85
|
// which point we can't mess with it anymore. Context menu is
|
84
86
|
// handled in onMouseDown for Gecko.
|
85
87
|
if (!gecko) connect(scroller, "contextmenu", onContextMenu);
|
86
|
-
connect(
|
87
|
-
|
88
|
+
connect(scroller, "scroll", function() {
|
89
|
+
updateDisplay([]);
|
90
|
+
if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
|
91
|
+
if (options.onScroll) options.onScroll(instance);
|
92
|
+
});
|
88
93
|
connect(window, "resize", function() {updateDisplay(true);});
|
89
94
|
connect(input, "keyup", operation(onKeyUp));
|
90
95
|
connect(input, "keydown", operation(onKeyDown));
|
@@ -111,7 +116,7 @@ var CodeMirror = (function() {
|
|
111
116
|
// range checking and/or clipping. operation is used to wrap the
|
112
117
|
// call so that changes it makes are tracked, and the display is
|
113
118
|
// updated afterwards.
|
114
|
-
var instance = {
|
119
|
+
var instance = wrapper.CodeMirror = {
|
115
120
|
getValue: getValue,
|
116
121
|
setValue: operation(setValue),
|
117
122
|
getSelection: getSelection,
|
@@ -119,7 +124,8 @@ var CodeMirror = (function() {
|
|
119
124
|
focus: function(){focusInput(); onFocus(); fastPoll();},
|
120
125
|
setOption: function(option, value) {
|
121
126
|
options[option] = value;
|
122
|
-
if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber")
|
127
|
+
if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber")
|
128
|
+
operation(gutterChanged)();
|
123
129
|
else if (option == "mode" || option == "indentUnit") loadMode();
|
124
130
|
else if (option == "readOnly" && value == "nocursor") input.blur();
|
125
131
|
else if (option == "theme") scroller.className = scroller.className.replace(/cm-s-\w+/, "cm-s-" + value);
|
@@ -152,18 +158,17 @@ var CodeMirror = (function() {
|
|
152
158
|
},
|
153
159
|
getSearchCursor: function(query, pos, caseFold) {return new SearchCursor(query, pos, caseFold);},
|
154
160
|
markText: operation(function(a, b, c){return operation(markText(a, b, c));}),
|
155
|
-
setMarker: addGutterMarker,
|
156
|
-
clearMarker: removeGutterMarker,
|
161
|
+
setMarker: operation(addGutterMarker),
|
162
|
+
clearMarker: operation(removeGutterMarker),
|
157
163
|
setLineClass: operation(setLineClass),
|
158
164
|
lineInfo: lineInfo,
|
159
|
-
addWidget: function(pos, node, scroll,
|
165
|
+
addWidget: function(pos, node, scroll, vert, horiz) {
|
160
166
|
pos = localCoords(clipPos(pos));
|
161
167
|
var top = pos.yBot, left = pos.x;
|
162
168
|
node.style.position = "absolute";
|
163
169
|
code.appendChild(node);
|
164
|
-
|
165
|
-
if (
|
166
|
-
else if (where == "near") {
|
170
|
+
if (vert == "over") top = pos.y;
|
171
|
+
else if (vert == "near") {
|
167
172
|
var vspace = Math.max(scroller.offsetHeight, lines.length * lineHeight()),
|
168
173
|
hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft();
|
169
174
|
if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight)
|
@@ -172,7 +177,15 @@ var CodeMirror = (function() {
|
|
172
177
|
left = hspace - node.offsetWidth;
|
173
178
|
}
|
174
179
|
node.style.top = (top + paddingTop()) + "px";
|
175
|
-
node.style.left =
|
180
|
+
node.style.left = node.style.right = "";
|
181
|
+
if (horiz == "right") {
|
182
|
+
left = code.clientWidth - node.offsetWidth;
|
183
|
+
node.style.right = "0px";
|
184
|
+
} else {
|
185
|
+
if (horiz == "left") left = 0;
|
186
|
+
else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2;
|
187
|
+
node.style.left = (left + paddingLeft()) + "px";
|
188
|
+
}
|
176
189
|
if (scroll)
|
177
190
|
scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight);
|
178
191
|
},
|
@@ -212,6 +225,7 @@ var CodeMirror = (function() {
|
|
212
225
|
updateLines(top, {line: lines.length - 1, ch: lines[lines.length-1].text.length},
|
213
226
|
splitLines(code), top, top);
|
214
227
|
history = new History();
|
228
|
+
updateInput = true;
|
215
229
|
}
|
216
230
|
function getValue(code) {
|
217
231
|
var text = [];
|
@@ -224,12 +238,12 @@ var CodeMirror = (function() {
|
|
224
238
|
// Check whether this is a click in a widget
|
225
239
|
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
226
240
|
if (n.parentNode == code && n != mover) return;
|
227
|
-
|
241
|
+
|
228
242
|
// First, see if this is a click in the gutter
|
229
243
|
for (var n = e_target(e); n != wrapper; n = n.parentNode)
|
230
244
|
if (n.parentNode == gutterText) {
|
231
245
|
if (options.onGutterClick)
|
232
|
-
options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom);
|
246
|
+
options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e);
|
233
247
|
return e_preventDefault(e);
|
234
248
|
}
|
235
249
|
|
@@ -249,18 +263,26 @@ var CodeMirror = (function() {
|
|
249
263
|
if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;}
|
250
264
|
|
251
265
|
if (!focused) onFocus();
|
252
|
-
|
253
|
-
|
266
|
+
|
267
|
+
var now = +new Date;
|
268
|
+
if (lastDoubleClick > now - 400) {
|
269
|
+
e_preventDefault(e);
|
270
|
+
return selectLine(start.line);
|
271
|
+
} else if (lastClick > now - 400) {
|
272
|
+
lastDoubleClick = now;
|
273
|
+
e_preventDefault(e);
|
274
|
+
return selectWordAt(start);
|
275
|
+
} else { lastClick = now; }
|
254
276
|
|
255
|
-
setCursor(start.line, start.ch, true);
|
256
277
|
var last = start, going;
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
updateInput = true;
|
262
|
-
move(); up();
|
278
|
+
if (dragAndDrop && !posEq(sel.from, sel.to) &&
|
279
|
+
!posLess(start, sel.from) && !posLess(sel.to, start)) {
|
280
|
+
// Let the drag handler handle this.
|
281
|
+
return;
|
263
282
|
}
|
283
|
+
e_preventDefault(e);
|
284
|
+
setCursor(start.line, start.ch, true);
|
285
|
+
|
264
286
|
function extend(e) {
|
265
287
|
var cur = posFromMouse(e, true);
|
266
288
|
if (cur && !posEq(cur, last)) {
|
@@ -284,16 +306,11 @@ var CodeMirror = (function() {
|
|
284
306
|
var cur = posFromMouse(e);
|
285
307
|
if (cur) setSelectionUser(start, cur);
|
286
308
|
e_preventDefault(e);
|
287
|
-
|
309
|
+
focusInput();
|
310
|
+
updateInput = true;
|
311
|
+
move(); up();
|
288
312
|
}), true);
|
289
313
|
}
|
290
|
-
function onDblClick(e) {
|
291
|
-
var pos = posFromMouse(e);
|
292
|
-
if (!pos) return;
|
293
|
-
selectWordAt(pos);
|
294
|
-
e_preventDefault(e);
|
295
|
-
lastDoubleClick = +new Date;
|
296
|
-
}
|
297
314
|
function onDrop(e) {
|
298
315
|
e.preventDefault();
|
299
316
|
var pos = posFromMouse(e, true), files = e.dataTransfer.files;
|
@@ -318,6 +335,13 @@ var CodeMirror = (function() {
|
|
318
335
|
catch(e){}
|
319
336
|
}
|
320
337
|
}
|
338
|
+
function onDragStart(e) {
|
339
|
+
var txt = getSelection();
|
340
|
+
// This will reset escapeElement
|
341
|
+
htmlEscape(txt);
|
342
|
+
e.dataTransfer.setDragImage(escapeElement, 0, 0);
|
343
|
+
e.dataTransfer.setData("Text", txt);
|
344
|
+
}
|
321
345
|
function onKeyDown(e) {
|
322
346
|
if (!focused) onFocus();
|
323
347
|
|
@@ -690,9 +714,10 @@ var CodeMirror = (function() {
|
|
690
714
|
else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;}
|
691
715
|
|
692
716
|
var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
|
693
|
-
|
717
|
+
var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
|
718
|
+
if (x1 < screenleft + gutterw) {
|
694
719
|
if (x1 < 50) x1 = 0;
|
695
|
-
scroller.scrollLeft = Math.max(0, x1 - 10);
|
720
|
+
scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
|
696
721
|
scrolled = true;
|
697
722
|
}
|
698
723
|
else if (x2 > screenw + screenleft) {
|
@@ -760,6 +785,7 @@ var CodeMirror = (function() {
|
|
760
785
|
if (domPos != domEnd || pos != to) {
|
761
786
|
changedLines += Math.abs(to - pos);
|
762
787
|
updates.push({from: pos, to: to, domSize: domEnd - domPos, domStart: domPos});
|
788
|
+
if (to - pos != domEnd - domPos) gutterDirty = true;
|
763
789
|
}
|
764
790
|
|
765
791
|
if (!updates.length) return;
|
@@ -782,7 +808,7 @@ var CodeMirror = (function() {
|
|
782
808
|
lastHeight = scroller.clientHeight;
|
783
809
|
code.style.height = (lines.length * lineHeight() + 2 * paddingTop()) + "px";
|
784
810
|
}
|
785
|
-
if (different ||
|
811
|
+
if (different || gutterDirty) updateGutter();
|
786
812
|
|
787
813
|
if (maxWidth == null) maxWidth = stringWidth(maxLine);
|
788
814
|
if (maxWidth > scroller.clientWidth) {
|
@@ -883,11 +909,13 @@ var CodeMirror = (function() {
|
|
883
909
|
if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild);
|
884
910
|
gutter.style.display = "";
|
885
911
|
lineSpace.style.marginLeft = gutter.offsetWidth + "px";
|
912
|
+
gutterDirty = false;
|
886
913
|
}
|
887
914
|
function updateCursor() {
|
888
915
|
var head = sel.inverted ? sel.from : sel.to, lh = lineHeight();
|
889
916
|
var x = charX(head.line, head.ch);
|
890
|
-
|
917
|
+
var top = head.line * lh - scroller.scrollTop;
|
918
|
+
inputDiv.style.top = Math.max(Math.min(top, scroller.offsetHeight), 0) + "px";
|
891
919
|
inputDiv.style.left = (x - scroller.scrollLeft) + "px";
|
892
920
|
if (posEq(sel.from, sel.to)) {
|
893
921
|
cursor.style.top = (head.line - showingFrom) * lh + "px";
|
@@ -1057,7 +1085,7 @@ var CodeMirror = (function() {
|
|
1057
1085
|
function gutterChanged() {
|
1058
1086
|
var visible = options.gutter || options.lineNumbers;
|
1059
1087
|
gutter.style.display = visible ? "" : "none";
|
1060
|
-
if (visible)
|
1088
|
+
if (visible) gutterDirty = true;
|
1061
1089
|
else lineDiv.parentNode.style.marginLeft = 0;
|
1062
1090
|
}
|
1063
1091
|
|
@@ -1094,13 +1122,13 @@ var CodeMirror = (function() {
|
|
1094
1122
|
function addGutterMarker(line, text, className) {
|
1095
1123
|
if (typeof line == "number") line = lines[clipLine(line)];
|
1096
1124
|
line.gutterMarker = {text: text, style: className};
|
1097
|
-
|
1125
|
+
gutterDirty = true;
|
1098
1126
|
return line;
|
1099
1127
|
}
|
1100
1128
|
function removeGutterMarker(line) {
|
1101
1129
|
if (typeof line == "number") line = lines[clipLine(line)];
|
1102
1130
|
line.gutterMarker = null;
|
1103
|
-
|
1131
|
+
gutterDirty = true;
|
1104
1132
|
}
|
1105
1133
|
function setLineClass(line, className) {
|
1106
1134
|
if (typeof line == "number") {
|
@@ -1382,7 +1410,10 @@ var CodeMirror = (function() {
|
|
1382
1410
|
var reScroll = false;
|
1383
1411
|
if (selectionChanged) reScroll = !scrollCursorIntoView();
|
1384
1412
|
if (changes.length) updateDisplay(changes);
|
1385
|
-
else
|
1413
|
+
else {
|
1414
|
+
if (selectionChanged) updateCursor();
|
1415
|
+
if (gutterDirty) updateGutter();
|
1416
|
+
}
|
1386
1417
|
if (reScroll) scrollCursorIntoView();
|
1387
1418
|
if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
|
1388
1419
|
|
@@ -1547,6 +1578,7 @@ var CodeMirror = (function() {
|
|
1547
1578
|
onKeyEvent: null,
|
1548
1579
|
lineNumbers: false,
|
1549
1580
|
gutter: false,
|
1581
|
+
fixedGutter: false,
|
1550
1582
|
firstLineNumber: 1,
|
1551
1583
|
readOnly: false,
|
1552
1584
|
smartHome: true,
|
@@ -1595,7 +1627,7 @@ var CodeMirror = (function() {
|
|
1595
1627
|
CodeMirror.listMIMEs = function() {
|
1596
1628
|
var list = [];
|
1597
1629
|
for (var m in mimeModes)
|
1598
|
-
if (mimeModes.propertyIsEnumerable(m)) list.push(m);
|
1630
|
+
if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]});
|
1599
1631
|
return list;
|
1600
1632
|
};
|
1601
1633
|
|
@@ -1978,6 +2010,15 @@ var CodeMirror = (function() {
|
|
1978
2010
|
pre.innerHTML = " "; return !pre.innerHTML;
|
1979
2011
|
})();
|
1980
2012
|
|
2013
|
+
// Detect drag-and-drop
|
2014
|
+
var dragAndDrop = (function() {
|
2015
|
+
// IE8 has ondragstart and ondrop properties, but doesn't seem to
|
2016
|
+
// actually support ondragstart the way it's supposed to work.
|
2017
|
+
if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
|
2018
|
+
var div = document.createElement('div');
|
2019
|
+
return "ondragstart" in div && "ondrop" in div;
|
2020
|
+
})();
|
2021
|
+
|
1981
2022
|
var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
|
1982
2023
|
var ie = /MSIE \d/.test(navigator.userAgent);
|
1983
2024
|
var safari = /Apple Computer/.test(navigator.vendor);
|
@@ -2153,5 +2194,4 @@ var CodeMirror = (function() {
|
|
2153
2194
|
CodeMirror.defineMIME("text/plain", "null");
|
2154
2195
|
|
2155
2196
|
return CodeMirror;
|
2156
|
-
})()
|
2157
|
-
;
|
2197
|
+
})();
|
@@ -0,0 +1,207 @@
|
|
1
|
+
/**
|
2
|
+
* Author: Hans Engel
|
3
|
+
* Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun)
|
4
|
+
*/
|
5
|
+
CodeMirror.defineMode("clojure", function (config, mode) {
|
6
|
+
var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", TAG = "tag",
|
7
|
+
ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD="keyword";
|
8
|
+
var INDENT_WORD_SKIP = 2, KEYWORDS_SKIP = 1;
|
9
|
+
|
10
|
+
function makeKeywords(str) {
|
11
|
+
var obj = {}, words = str.split(" ");
|
12
|
+
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
|
13
|
+
return obj;
|
14
|
+
}
|
15
|
+
|
16
|
+
var atoms = makeKeywords("true false nil");
|
17
|
+
|
18
|
+
var keywords = makeKeywords(
|
19
|
+
// Control structures
|
20
|
+
"defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle" +
|
21
|
+
|
22
|
+
// Built-ins
|
23
|
+
"* *1 *2 *3 *agent* *allow-unresolved-vars* *assert *clojure-version* *command-line-args* *compile-files* *compile-path* *e *err* *file* *flush-on-newline* *in* *macro-meta* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *use-context-classloader* *warn-on-reflection* + - / < <= = == > >= accessor aclone agent agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec decimal? declare definline defmacro defmethod defmulti defn defn- defonce defstruct delay delay? deliver deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall doc dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq eval even? every? extend extend-protocol extend-type extends? extenders false? ffirst file-seq filter find find-doc find-ns find-var first float float-array float? floats flush fn fn? fnext for force format future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator hash hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map? mapcat max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod name namespace neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext num number? odd? or parents partial partition pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-doc print-dup print-method print-namespace-doc print-simple print-special-doc print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string reify reduce ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure release-pending-sends rem remove remove-method remove-ns repeat repeatedly replace replicate require reset! reset-meta! resolve rest resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-validator! set? short short-array shorts shutdown-agents slurp some sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-form-anchor special-symbol? split-at split-with str stream? string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync syntax-symbol-anchor take take-last take-nth take-while test the-ns time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-dec unchecked-divide unchecked-inc unchecked-multiply unchecked-negate unchecked-remainder unchecked-subtract underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision xml-seq");
|
24
|
+
|
25
|
+
var indentKeys = makeKeywords(
|
26
|
+
// Built-ins
|
27
|
+
"ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch" +
|
28
|
+
|
29
|
+
// Binding forms
|
30
|
+
"let letfn binding loop for doseq dotimes when-let if-let" +
|
31
|
+
|
32
|
+
// Data structures
|
33
|
+
"defstruct struct-map assoc" +
|
34
|
+
|
35
|
+
// clojure.test
|
36
|
+
"testing deftest" +
|
37
|
+
|
38
|
+
// contrib
|
39
|
+
"handler-case handle dotrace deftrace");
|
40
|
+
|
41
|
+
var tests = {
|
42
|
+
digit: /\d/,
|
43
|
+
digit_or_colon: /[\d:]/,
|
44
|
+
hex: /[0-9a-fA-F]/,
|
45
|
+
sign: /[+-]/,
|
46
|
+
exponent: /[eE]/,
|
47
|
+
keyword_char: /[^\s\(\[\;\)\]]/,
|
48
|
+
basic: /[\w\$_\-]/,
|
49
|
+
lang_keyword: /[\w*+!\-_?:\/]/
|
50
|
+
};
|
51
|
+
|
52
|
+
function stateStack(indent, type, prev) { // represents a state stack object
|
53
|
+
this.indent = indent;
|
54
|
+
this.type = type;
|
55
|
+
this.prev = prev;
|
56
|
+
}
|
57
|
+
|
58
|
+
function pushStack(state, indent, type) {
|
59
|
+
state.indentStack = new stateStack(indent, type, state.indentStack);
|
60
|
+
}
|
61
|
+
|
62
|
+
function popStack(state) {
|
63
|
+
state.indentStack = state.indentStack.prev;
|
64
|
+
}
|
65
|
+
|
66
|
+
function isNumber(ch, stream){
|
67
|
+
// hex
|
68
|
+
if ( ch === '0' && 'x' == stream.peek().toLowerCase() ) {
|
69
|
+
stream.eat('x');
|
70
|
+
stream.eatWhile(tests.hex);
|
71
|
+
return true;
|
72
|
+
}
|
73
|
+
|
74
|
+
// leading sign
|
75
|
+
if ( ch == '+' || ch == '-' ) {
|
76
|
+
stream.eat(tests.sign);
|
77
|
+
ch = stream.next();
|
78
|
+
}
|
79
|
+
|
80
|
+
if ( tests.digit.test(ch) ) {
|
81
|
+
stream.eat(ch);
|
82
|
+
stream.eatWhile(tests.digit);
|
83
|
+
|
84
|
+
if ( '.' == stream.peek() ) {
|
85
|
+
stream.eat('.');
|
86
|
+
stream.eatWhile(tests.digit);
|
87
|
+
}
|
88
|
+
|
89
|
+
if ( 'e' == stream.peek().toLowerCase() ) {
|
90
|
+
stream.eat(tests.exponent);
|
91
|
+
stream.eat(tests.sign);
|
92
|
+
stream.eatWhile(tests.digit);
|
93
|
+
}
|
94
|
+
|
95
|
+
return true;
|
96
|
+
}
|
97
|
+
|
98
|
+
return false;
|
99
|
+
}
|
100
|
+
|
101
|
+
return {
|
102
|
+
startState: function () {
|
103
|
+
return {
|
104
|
+
indentStack: null,
|
105
|
+
indentation: 0,
|
106
|
+
mode: false,
|
107
|
+
};
|
108
|
+
},
|
109
|
+
|
110
|
+
token: function (stream, state) {
|
111
|
+
if (state.indentStack == null && stream.sol()) {
|
112
|
+
// update indentation, but only if indentStack is empty
|
113
|
+
state.indentation = stream.indentation();
|
114
|
+
}
|
115
|
+
|
116
|
+
// skip spaces
|
117
|
+
if (stream.eatSpace()) {
|
118
|
+
return null;
|
119
|
+
}
|
120
|
+
var returnType = null;
|
121
|
+
|
122
|
+
switch(state.mode){
|
123
|
+
case "string": // multi-line string parsing mode
|
124
|
+
var next, escaped = false;
|
125
|
+
while ((next = stream.next()) != null) {
|
126
|
+
if (next == "\"" && !escaped) {
|
127
|
+
|
128
|
+
state.mode = false;
|
129
|
+
break;
|
130
|
+
}
|
131
|
+
escaped = !escaped && next == "\\";
|
132
|
+
}
|
133
|
+
returnType = STRING; // continue on in string mode
|
134
|
+
break;
|
135
|
+
default: // default parsing mode
|
136
|
+
var ch = stream.next();
|
137
|
+
|
138
|
+
if (ch == "\"") {
|
139
|
+
state.mode = "string";
|
140
|
+
returnType = STRING;
|
141
|
+
} else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) {
|
142
|
+
returnType = ATOM;
|
143
|
+
} else if (ch == ";") { // comment
|
144
|
+
stream.skipToEnd(); // rest of the line is a comment
|
145
|
+
returnType = COMMENT;
|
146
|
+
} else if (isNumber(ch,stream)){
|
147
|
+
returnType = NUMBER;
|
148
|
+
} else if (ch == "(" || ch == "[") {
|
149
|
+
var keyWord = ''; var indentTemp = stream.column();
|
150
|
+
/**
|
151
|
+
Either
|
152
|
+
(indent-word ..
|
153
|
+
(non-indent-word ..
|
154
|
+
(;something else, bracket, etc.
|
155
|
+
*/
|
156
|
+
|
157
|
+
while ((letter = stream.eat(tests.keyword_char)) != null) {
|
158
|
+
keyWord += letter;
|
159
|
+
}
|
160
|
+
|
161
|
+
if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word
|
162
|
+
|
163
|
+
pushStack(state, indentTemp + INDENT_WORD_SKIP, ch);
|
164
|
+
} else { // non-indent word
|
165
|
+
// we continue eating the spaces
|
166
|
+
stream.eatSpace();
|
167
|
+
if (stream.eol() || stream.peek() == ";") {
|
168
|
+
// nothing significant after
|
169
|
+
// we restart indentation 1 space after
|
170
|
+
pushStack(state, indentTemp + 1, ch);
|
171
|
+
} else {
|
172
|
+
pushStack(state, indentTemp + stream.current().length, ch); // else we match
|
173
|
+
}
|
174
|
+
}
|
175
|
+
stream.backUp(stream.current().length - 1); // undo all the eating
|
176
|
+
|
177
|
+
returnType = BRACKET;
|
178
|
+
} else if (ch == ")" || ch == "]") {
|
179
|
+
returnType = BRACKET;
|
180
|
+
if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : "[")) {
|
181
|
+
popStack(state);
|
182
|
+
}
|
183
|
+
} else if ( ch == ":" ) {
|
184
|
+
stream.eatWhile(tests.lang_keyword);
|
185
|
+
return TAG;
|
186
|
+
} else {
|
187
|
+
stream.eatWhile(tests.basic);
|
188
|
+
|
189
|
+
if (keywords && keywords.propertyIsEnumerable(stream.current())) {
|
190
|
+
returnType = BUILTIN;
|
191
|
+
} else if ( atoms && atoms.propertyIsEnumerable(stream.current()) ) {
|
192
|
+
returnType = ATOM;
|
193
|
+
} else returnType = null;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
return returnType;
|
198
|
+
},
|
199
|
+
|
200
|
+
indent: function (state, textAfter) {
|
201
|
+
if (state.indentStack == null) return state.indentation;
|
202
|
+
return state.indentStack.indent;
|
203
|
+
}
|
204
|
+
};
|
205
|
+
});
|
206
|
+
|
207
|
+
CodeMirror.defineMIME("text/x-clojure", "clojure");
|