codemirror-rails 0.2.3 → 0.3.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.
- 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");
|