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 CHANGED
@@ -1,9 +1,42 @@
1
- CodeMirror for Rails 3
2
- ======================
1
+ # codemirror-rails
3
2
 
4
- Generator to install current version of CodeMirror 2 into a
5
- Rails 3 project.
3
+ Wire up the [CodeMirror](http://codemirror.net/) assets for your Rails
4
+ applications.
6
5
 
7
- ```
8
- rails generate codemirror:install
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
@@ -2,6 +2,8 @@ module Codemirror
2
2
  module Rails
3
3
  if ::Rails.version < "3.1"
4
4
  require 'codemirror/rails/railtie'
5
+ else
6
+ require 'codemirror/rails/engine'
5
7
  end
6
8
  require 'codemirror/rails/version'
7
9
  end
@@ -0,0 +1,6 @@
1
+ module Codemirror
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '0.2.3'
4
- CODEMIRROR_VERSION = '2.13'
3
+ VERSION = '0.3.0'
4
+ CODEMIRROR_VERSION = '2.15'
5
5
  end
6
6
  end
@@ -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"></textarea></div>' +
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">&#160;</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(code, "dblclick", operation(onDblClick));
87
- connect(scroller, "scroll", function() {updateDisplay([]); if (options.onScroll) options.onScroll(instance);});
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") gutterChanged();
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, where) {
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
- node.style.left = left + "px";
165
- if (where == "over") top = pos.y;
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 = (left + paddingLeft()) + "px";
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
- var ld = lastDoubleClick; lastDoubleClick = null;
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
- e_preventDefault(e);
253
- if (ld && +new Date - ld < 400) return selectLine(start.line);
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
- // And then we have to see if it's a drag event, in which case
258
- // the dragged-over text must be selected.
259
- function end() {
260
- focusInput();
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
- end();
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
- if (x1 < screenleft) {
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 || updates.length) updateGutter();
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
- inputDiv.style.top = (head.line * lh - scroller.scrollTop) + "px";
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) updateGutter();
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
- updateGutter();
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
- updateGutter();
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 if (selectionChanged) updateCursor();
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");